mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-17 23:17:38 +00:00
Compare commits
27 Commits
mc-1.19.x
...
v1.20.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
56d97630e8 | ||
![]() |
e660192f08 | ||
![]() |
4e82bd352d | ||
![]() |
07113c3e9b | ||
![]() |
d562a051c7 | ||
![]() |
6ac09742fc | ||
![]() |
5dd6b9a637 | ||
![]() |
8fb1dd346c | ||
![]() |
0f6ea3deaf | ||
![]() |
13ed422bd5 | ||
![]() |
5b58271b92 | ||
![]() |
4e42394f33 | ||
![]() |
3371c4651c | ||
![]() |
e6bc1e4e27 | ||
![]() |
ae50f900af | ||
![]() |
48889ceb89 | ||
![]() |
8f1bf4341c | ||
![]() |
9a48b53a83 | ||
![]() |
9519448e43 | ||
![]() |
915b6f9d81 | ||
![]() |
a98f3b2a4c | ||
![]() |
ebaf49508f | ||
![]() |
c45fc94752 | ||
![]() |
fd1f6dda32 | ||
![]() |
5d6389dc50 | ||
![]() |
1ece2aa23b | ||
![]() |
ff1e5f6823 |
@@ -52,7 +52,7 @@ dependencies {
|
||||
implementation(libs.forgeGradle)
|
||||
implementation(libs.librarian)
|
||||
implementation(libs.minotaur)
|
||||
implementation(libs.quiltflower)
|
||||
implementation(libs.vineflower)
|
||||
implementation(libs.vanillaGradle)
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,7 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
||||
plugins {
|
||||
`java-library`
|
||||
id("fabric-loom")
|
||||
id("io.github.juuxel.loom-quiltflower")
|
||||
id("io.github.juuxel.loom-vineflower")
|
||||
id("cc-tweaked.java-convention")
|
||||
}
|
||||
|
||||
|
@@ -59,6 +59,7 @@ repositories {
|
||||
includeGroup("cc.tweaked")
|
||||
includeModule("org.squiddev", "Cobalt")
|
||||
// Things we mirror
|
||||
includeGroup("commoble.morered")
|
||||
includeGroup("dev.architectury")
|
||||
includeGroup("dev.emi")
|
||||
includeGroup("maven.modrinth")
|
||||
@@ -190,6 +191,7 @@ spotless {
|
||||
|
||||
val ktlintConfig = mapOf(
|
||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||
"ktlint_standard_class-naming" to "disabled",
|
||||
"ij_kotlin_allow_trailing_comma" to "true",
|
||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||
)
|
||||
|
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.108.0
|
||||
modVersion=1.108.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.19.4
|
||||
mcVersion=1.20.1
|
||||
|
@@ -7,17 +7,17 @@
|
||||
# Minecraft
|
||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||
fabric-api = "0.86.1+1.19.4"
|
||||
fabric-api = "0.86.1+1.20.1"
|
||||
fabric-loader = "0.14.21"
|
||||
forge = "45.0.42"
|
||||
forge = "47.1.0"
|
||||
forgeSpi = "6.0.0"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2023.06.26"
|
||||
parchmentMc = "1.19.4"
|
||||
parchment = "2023.08.20"
|
||||
parchmentMc = "1.20.1"
|
||||
|
||||
# Normal dependencies
|
||||
asm = "9.3"
|
||||
autoService = "1.0.1"
|
||||
asm = "9.5"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.32.0"
|
||||
cobalt = "0.7.3"
|
||||
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
|
||||
@@ -29,45 +29,46 @@ jzlib = "1.1.3"
|
||||
kotlin = "1.8.10"
|
||||
kotlin-coroutines = "1.6.4"
|
||||
netty = "4.1.82.Final"
|
||||
nightConfig = "3.6.5"
|
||||
nightConfig = "3.6.7"
|
||||
slf4j = "1.7.36"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.8+1.19.4"
|
||||
fabricPermissions = "0.2.20221016"
|
||||
iris = "1.5.2+1.19.4"
|
||||
jei = "13.1.0.11"
|
||||
modmenu = "6.1.0-rc.1"
|
||||
emi = "1.0.8+1.20.1"
|
||||
fabricPermissions = "0.3.20230723"
|
||||
iris = "1.6.4+1.20"
|
||||
jei = "15.2.0.22"
|
||||
modmenu = "7.1.0"
|
||||
moreRed = "4.0.0.4"
|
||||
oculus = "1.2.5"
|
||||
rei = "10.0.578"
|
||||
rei = "12.0.626"
|
||||
rubidium = "0.6.1"
|
||||
sodium = "mc1.19.4-0.4.10"
|
||||
sodium = "mc1.20-0.4.10"
|
||||
|
||||
# Testing
|
||||
byteBuddy = "1.14.2"
|
||||
byteBuddy = "1.14.7"
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.7.2"
|
||||
junit = "5.9.2"
|
||||
jqwik = "1.7.4"
|
||||
junit = "5.10.0"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.0"
|
||||
checkstyle = "10.3.4"
|
||||
checkstyle = "10.12.3"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.18.0"
|
||||
errorProne-plugin = "3.0.1"
|
||||
errorProne-core = "2.21.1"
|
||||
errorProne-plugin = "3.1.0"
|
||||
fabric-loom = "1.3.7"
|
||||
forgeGradle = "6.0.8"
|
||||
githubRelease = "2.2.12"
|
||||
ideaExt = "1.1.6"
|
||||
githubRelease = "2.4.1"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-40-g975cbc3"
|
||||
librarian = "1.+"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.+"
|
||||
nullAway = "0.9.9"
|
||||
quiltflower = "1.10.0"
|
||||
spotless = "6.17.0"
|
||||
spotless = "6.21.0"
|
||||
taskTree = "2.1.1"
|
||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||
vineflower = "1.11.0"
|
||||
|
||||
[libraries]
|
||||
# Normal dependencies
|
||||
@@ -97,11 +98,12 @@ fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-l
|
||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||
jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" }
|
||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
|
||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||
@@ -135,9 +137,9 @@ kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.
|
||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||
quiltflower = { module = "io.github.juuxel:loom-quiltflower", version.ref = "quiltflower" }
|
||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||
vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vineflower" }
|
||||
|
||||
[plugins]
|
||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||
@@ -153,7 +155,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||
|
||||
# Minecraft
|
||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
||||
externalMods-forge-runtime = ["jei-forge"]
|
||||
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||
|
1228
package-lock.json
generated
1228
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,15 +15,15 @@
|
||||
"@rollup/plugin-url": "^8.0.1",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"glob": "^9.3.0",
|
||||
"glob": "^10.3.4",
|
||||
"react-dom": "^18.1.0",
|
||||
"react": "^18.1.0",
|
||||
"rehype-highlight": "^6.0.0",
|
||||
"rehype-react": "^7.1.1",
|
||||
"rehype": "^12.0.1",
|
||||
"rehype": "^12.0.0",
|
||||
"requirejs": "^2.3.6",
|
||||
"rollup": "^3.19.1",
|
||||
"ts-node": "^10.8.0",
|
||||
"typescript": "^4.0.5"
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ import org.joml.Matrix4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class TurtleUpgradeModellers {
|
||||
final class TurtleUpgradeModellers {
|
||||
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
||||
private static final Transformation rightTransform = getMatrixFor(0.4065f);
|
||||
|
||||
@@ -35,7 +35,7 @@ class TurtleUpgradeModellers {
|
||||
|
||||
static final TurtleUpgradeModeller<ITurtleUpgrade> UPGRADE_ITEM = new UpgradeItemModeller();
|
||||
|
||||
private static class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||
@Override
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
@@ -30,14 +29,6 @@ import java.util.function.Function;
|
||||
* @see PocketUpgradeDataProvider
|
||||
*/
|
||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @deprecated Use {@link #registryId()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
|
@@ -16,7 +16,6 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -71,29 +70,6 @@ public interface ITurtleAccess {
|
||||
*/
|
||||
boolean teleportTo(Level world, BlockPos pos);
|
||||
|
||||
/**
|
||||
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
|
||||
* This will shift when the turtle is moving.
|
||||
*
|
||||
* @param f The subframe fraction.
|
||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
||||
* @see #getVisualYaw(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Vec3 getVisualPosition(float f);
|
||||
|
||||
/**
|
||||
* Returns the yaw the turtle is facing when it is rendered.
|
||||
*
|
||||
* @param f The subframe fraction.
|
||||
* @return The yaw the turtle is facing.
|
||||
* @see #getVisualPosition(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
float getVisualYaw(float f);
|
||||
|
||||
/**
|
||||
* Returns the world direction the turtle is currently facing.
|
||||
*
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
@@ -65,14 +64,6 @@ import java.util.function.Function;
|
||||
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
|
||||
*/
|
||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @deprecated Use {@link #registryId()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
@@ -19,6 +18,7 @@ import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -124,10 +124,10 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(stack);
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(stack, mouseX, mouseY);
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(graphics, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,7 +147,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
|
||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||
// Skip rendering labels.
|
||||
}
|
||||
|
||||
|
@@ -4,15 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
@@ -39,12 +37,10 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderBg(PoseStack stack, float partialTicks, int mouseX, int mouseY) {
|
||||
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
// Draw a border around the terminal
|
||||
var terminal = getTerminal();
|
||||
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
|
||||
var spriteRenderer = SpriteRenderer.createForGui(stack, buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||
var computerTextures = GuiSprites.getComputerTextures(family);
|
||||
|
||||
ComputerBorderRenderer.render(
|
||||
@@ -52,6 +48,6 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
||||
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
|
||||
);
|
||||
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
|
||||
buffers.endBatch();
|
||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||
}
|
||||
}
|
||||
|
@@ -4,9 +4,8 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -23,16 +22,14 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
||||
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(transform);
|
||||
super.render(transform, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(transform, mouseX, mouseY);
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(graphics, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
@@ -4,10 +4,8 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.toasts.Toast;
|
||||
import net.minecraft.client.gui.components.toasts.ToastComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -73,55 +71,52 @@ public class ItemToast implements Toast {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Visibility render(PoseStack transform, ToastComponent component, long time) {
|
||||
public Visibility render(GuiGraphics graphics, ToastComponent component, long time) {
|
||||
if (isNew) {
|
||||
|
||||
firstDisplay = time;
|
||||
isNew = false;
|
||||
}
|
||||
|
||||
RenderSystem.setShaderTexture(0, TEXTURE);
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
if (width == 160 && message.size() <= 1) {
|
||||
GuiComponent.blit(transform, 0, 0, 0, 64, width, height());
|
||||
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
|
||||
} else {
|
||||
|
||||
var height = height();
|
||||
|
||||
var bottom = Math.min(4, height - 28);
|
||||
renderBackgroundRow(transform, component, width, 0, 0, 28);
|
||||
renderBackgroundRow(graphics, width, 0, 0, 28);
|
||||
|
||||
for (var i = 28; i < height - bottom; i += 10) {
|
||||
renderBackgroundRow(transform, component, width, 16, i, Math.min(16, height - i - bottom));
|
||||
renderBackgroundRow(graphics, width, 16, i, Math.min(16, height - i - bottom));
|
||||
}
|
||||
|
||||
renderBackgroundRow(transform, component, width, 32 - bottom, height - bottom, bottom);
|
||||
renderBackgroundRow(graphics, width, 32 - bottom, height - bottom, bottom);
|
||||
}
|
||||
|
||||
var textX = MARGIN;
|
||||
if (!stack.isEmpty()) {
|
||||
textX += MARGIN + IMAGE_SIZE;
|
||||
component.getMinecraft().getItemRenderer().renderAndDecorateFakeItem(transform, stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
||||
graphics.renderFakeItem(stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
||||
}
|
||||
|
||||
component.getMinecraft().font.draw(transform, title, textX, MARGIN, 0xff500050);
|
||||
graphics.drawString(component.getMinecraft().font, title, textX, MARGIN, 0xff500050, false);
|
||||
for (var i = 0; i < message.size(); ++i) {
|
||||
component.getMinecraft().font.draw(transform, message.get(i), textX, (float) (LINE_SPACING + (i + 1) * LINE_SPACING), 0xff000000);
|
||||
graphics.drawString(component.getMinecraft().font, message.get(i), textX, LINE_SPACING + (i + 1) * LINE_SPACING, 0xff000000, false);
|
||||
}
|
||||
|
||||
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
|
||||
}
|
||||
|
||||
private static void renderBackgroundRow(PoseStack transform, ToastComponent component, int x, int u, int y, int height) {
|
||||
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
|
||||
var leftOffset = 5;
|
||||
var rightOffset = Math.min(60, x - leftOffset);
|
||||
|
||||
GuiComponent.blit(transform, 0, y, 0, 32 + u, leftOffset, height);
|
||||
graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height);
|
||||
for (var k = leftOffset; k < x - rightOffset; k += 64) {
|
||||
GuiComponent.blit(transform, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
|
||||
graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
|
||||
}
|
||||
|
||||
GuiComponent.blit(transform, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
|
||||
graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -42,7 +42,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
passEvents = true; // Pass mouse vents through to the game's mouse handler.
|
||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||
// grabbing unsets.
|
||||
minecraft.mouseHandler.grabMouse();
|
||||
@@ -91,15 +90,15 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
||||
super.render(transform, mouseX, mouseY, partialTicks);
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
|
||||
var font = minecraft.font;
|
||||
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
||||
var y = 10.0f;
|
||||
var y = 10;
|
||||
for (var line : lines) {
|
||||
font.drawShadow(transform, line, (float) ((width / 2) - (minecraft.font.width(line) / 2)), y, 0xFFFFFF);
|
||||
y += 9.0f;
|
||||
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,9 +4,8 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.MultiLineLabel;
|
||||
@@ -86,20 +85,19 @@ public final class OptionScreen extends Screen {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(transform);
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
|
||||
// Render the actual texture.
|
||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
||||
blit(transform, x, y, 0, 0, innerWidth, PADDING);
|
||||
blit(transform,
|
||||
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
|
||||
graphics.blit(BACKGROUND,
|
||||
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
|
||||
innerWidth, PADDING
|
||||
);
|
||||
blit(transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
|
||||
graphics.blit(BACKGROUND, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
|
||||
|
||||
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
|
||||
super.render(transform, mouseX, mouseY, partialTicks);
|
||||
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(graphics, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -4,9 +4,8 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.shared.peripheral.printer.PrinterMenu;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -23,18 +22,16 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
RenderSystem.setShaderTexture(0, BACKGROUND);
|
||||
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
if (getMenu().isPrinting()) blit(transform, leftPos + 34, topPos + 21, 176, 0, 25, 45);
|
||||
if (getMenu().isPrinting()) graphics.blit(BACKGROUND, leftPos + 34, topPos + 21, 176, 0, 25, 45);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(stack);
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(stack, mouseX, mouseY);
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(graphics);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
renderTooltip(graphics, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,11 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -83,30 +82,27 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
// Draw the printout
|
||||
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
drawBorder(transform, renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(transform, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
|
||||
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
|
||||
renderer.endBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
// We must take the background further back in order to not overlap with our printed pages.
|
||||
stack.pushPose();
|
||||
stack.translate(0, 0, -1);
|
||||
renderBackground(stack);
|
||||
stack.popPose();
|
||||
graphics.pose().pushPose();
|
||||
graphics.pose().translate(0, 0, -1);
|
||||
renderBackground(graphics);
|
||||
graphics.pose().popPose();
|
||||
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
|
||||
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
|
||||
// Skip rendering labels.
|
||||
}
|
||||
}
|
||||
|
@@ -4,9 +4,6 @@
|
||||
|
||||
package dan200.computercraft.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
@@ -15,7 +12,7 @@ import dan200.computercraft.client.render.SpriteRenderer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -47,26 +44,25 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||
var advanced = family == ComputerFamily.ADVANCED;
|
||||
RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL);
|
||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||
var texture = advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
|
||||
graphics.blit(texture, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||
|
||||
// Render selected slot
|
||||
var slot = getMenu().getSelectedSlot();
|
||||
if (slot >= 0) {
|
||||
var slotX = slot % 4;
|
||||
var slotY = slot / 4;
|
||||
blit(transform,
|
||||
graphics.blit(texture,
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
||||
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
// Render sidebar
|
||||
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
var spriteRenderer = SpriteRenderer.createForGui(transform, buffers.getBuffer(RenderTypes.GUI_SPRITES));
|
||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
|
||||
buffers.endBatch();
|
||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||
}
|
||||
}
|
||||
|
@@ -5,9 +5,9 @@
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import it.unimi.dsi.fastutil.booleans.Boolean2ObjectFunction;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
@@ -42,21 +42,20 @@ public class DynamicImageButton extends Button {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWidget(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
var texture = this.texture.get(isHoveredOrFocused());
|
||||
RenderSystem.setShaderTexture(0, texture.atlasLocation());
|
||||
RenderSystem.disableDepthTest();
|
||||
|
||||
blit(stack, getX(), getY(), 0, width, height, texture);
|
||||
RenderSystem.disableDepthTest();
|
||||
graphics.blit(getX(), getY(), 0, width, height, texture);
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
var message = this.message.get();
|
||||
setMessage(message.message());
|
||||
setTooltip(message.tooltip());
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
public record HintedMessage(Component message, Tooltip tooltip) {
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
package dan200.computercraft.client.gui.widgets;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
@@ -12,6 +11,7 @@ import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
@@ -274,11 +274,11 @@ public class TerminalWidget extends AbstractWidget {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWidget(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
|
||||
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
if (!visible) return;
|
||||
|
||||
var bufferSource = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
|
||||
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||
var emitter = FixedWidthFontRenderer.toVertexConsumer(graphics.pose(), bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||
|
||||
FixedWidthFontRenderer.drawTerminal(
|
||||
emitter,
|
||||
|
@@ -47,7 +47,7 @@ public class ShaderMod {
|
||||
Optional<ShaderMod> get();
|
||||
}
|
||||
|
||||
private static class Storage {
|
||||
private static final class Storage {
|
||||
static final ShaderMod INSTANCE = ServiceLoader.load(Provider.class)
|
||||
.stream()
|
||||
.flatMap(x -> x.get().get().stream())
|
||||
|
@@ -52,7 +52,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
|
||||
var te = player.level.getBlockEntity(pos);
|
||||
var te = player.level().getBlockEntity(pos);
|
||||
if (!(te instanceof MonitorBlockEntity monitor)) return;
|
||||
|
||||
monitor.read(terminal);
|
||||
|
@@ -4,14 +4,14 @@
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
/**
|
||||
* A {@link GuiComponent}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
|
||||
* A {@link GuiGraphics}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
|
||||
* renderer).
|
||||
* <p>
|
||||
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
|
||||
@@ -34,8 +34,11 @@ public class SpriteRenderer {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public static SpriteRenderer createForGui(PoseStack stack, VertexConsumer builder) {
|
||||
return new SpriteRenderer(stack.last().pose(), builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
|
||||
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
|
||||
return new SpriteRenderer(
|
||||
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
|
||||
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +55,7 @@ public class SpriteRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiComponent#blitNineSliced},
|
||||
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiGraphics#blitNineSliced},
|
||||
* the middle texture is stretched rather than repeated.
|
||||
*
|
||||
* @param sprite The texture to draw.
|
||||
@@ -77,7 +80,7 @@ public class SpriteRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiComponent#blitNineSliced},
|
||||
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiGraphics#blitNineSliced},
|
||||
* the middle texture is stretched rather than repeated.
|
||||
*
|
||||
* @param sprite The texture to draw.
|
||||
|
@@ -7,10 +7,7 @@ package dan200.computercraft.client.render.monitor;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.platform.MemoryTracker;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.blaze3d.vertex.*;
|
||||
import com.mojang.math.Axis;
|
||||
import dan200.computercraft.client.FrameInfo;
|
||||
import dan200.computercraft.client.integration.ShaderMod;
|
||||
@@ -170,7 +167,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
tboVertex(buffer, matrix, -xMargin, pixelHeight + yMargin);
|
||||
tboVertex(buffer, matrix, pixelWidth + xMargin, -yMargin);
|
||||
tboVertex(buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin);
|
||||
RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0);
|
||||
RenderTypes.MONITOR_TBO.end(buffer, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||
}
|
||||
case VBO -> {
|
||||
var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);
|
||||
|
@@ -25,6 +25,7 @@ public class DirectVertexBuffer extends VertexBuffer {
|
||||
private int actualIndexCount;
|
||||
|
||||
public DirectVertexBuffer() {
|
||||
super(Usage.STATIC);
|
||||
if (DirectBuffers.HAS_DSA) {
|
||||
RenderSystem.glDeleteBuffers(vertexBufferId);
|
||||
if (DirectBuffers.ON_LINUX) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
|
||||
|
@@ -139,8 +139,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
add("commands.computercraft.tp.synopsis", "Teleport to a specific computer.");
|
||||
add("commands.computercraft.tp.desc", "Teleport to the location of a computer. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123).");
|
||||
add("commands.computercraft.tp.action", "Teleport to this computer");
|
||||
add("commands.computercraft.tp.not_player", "Cannot open terminal for non-player");
|
||||
add("commands.computercraft.tp.not_there", "Cannot locate computer in the world");
|
||||
add("commands.computercraft.view.synopsis", "View the terminal of a computer.");
|
||||
add("commands.computercraft.view.desc", "Open the terminal of a computer, allowing remote control of a computer. This does not provide access to turtle's inventories. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123).");
|
||||
add("commands.computercraft.view.action", "View this computer");
|
||||
|
@@ -23,7 +23,7 @@ import net.minecraft.world.level.storage.loot.entries.LootItem;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||
import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
@@ -79,7 +79,7 @@ class LootTableProvider {
|
||||
}
|
||||
|
||||
private static void registerGeneric(BiConsumer<ResourceLocation, LootTable.Builder> add) {
|
||||
add.accept(CommonHooks.LOOT_TREASURE_DISK, LootTable.lootTable());
|
||||
add.accept(CommonHooks.TREASURE_DISK_LOOT, LootTable.lootTable());
|
||||
}
|
||||
|
||||
private static void selfDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
||||
@@ -98,7 +98,7 @@ class LootTableProvider {
|
||||
blockDrop(
|
||||
add, block,
|
||||
DynamicLoot.dynamicEntry(new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer")),
|
||||
AlternativeLootItemCondition.alternative(
|
||||
AnyOfCondition.anyOf(
|
||||
BlockNamedEntityLootCondition.BUILDER,
|
||||
HasComputerIdLootCondition.BUILDER,
|
||||
PlayerCreativeLootCondition.BUILDER.invert()
|
||||
|
@@ -88,6 +88,8 @@ class TagProvider {
|
||||
ModRegistry.Items.MONITOR_ADVANCED.get()
|
||||
);
|
||||
|
||||
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
|
||||
|
||||
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
|
||||
.add(Items.GLASS_BOTTLE)
|
||||
.addTag(ItemTags.BOATS);
|
||||
|
@@ -24,7 +24,7 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
private WiredNetworkImpl(HashSet<WiredNodeImpl> nodes) {
|
||||
private WiredNetworkImpl(Set<WiredNodeImpl> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ final class WiredNetworkImpl implements WiredNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<WiredNodeImpl> reachableNodes(WiredNodeImpl start) {
|
||||
private static Set<WiredNodeImpl> reachableNodes(WiredNodeImpl start) {
|
||||
Queue<WiredNodeImpl> enqueued = new ArrayDeque<>();
|
||||
var reachable = new HashSet<WiredNodeImpl>();
|
||||
|
||||
|
@@ -28,8 +28,6 @@ import net.minecraft.world.level.storage.loot.entries.LootTableReference;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@@ -75,9 +73,9 @@ public final class CommonHooks {
|
||||
MonitorWatcher.onWatch(chunk, player);
|
||||
}
|
||||
|
||||
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
|
||||
public static final ResourceLocation TREASURE_DISK_LOOT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
|
||||
|
||||
private static final Set<ResourceLocation> TABLES = new HashSet<>(Arrays.asList(
|
||||
private static final Set<ResourceLocation> TREASURE_DISK_LOOT_TABLES = Set.of(
|
||||
BuiltInLootTables.SIMPLE_DUNGEON,
|
||||
BuiltInLootTables.ABANDONED_MINESHAFT,
|
||||
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
||||
@@ -88,14 +86,15 @@ public final class CommonHooks {
|
||||
BuiltInLootTables.IGLOO_CHEST,
|
||||
BuiltInLootTables.WOODLAND_MANSION,
|
||||
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
||||
));
|
||||
|
||||
);
|
||||
|
||||
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
|
||||
if (!lootTable.getNamespace().equals("minecraft") || !TABLES.contains(lootTable)) return null;
|
||||
if (!lootTable.getNamespace().equals("minecraft") || !TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LootPool.lootPool()
|
||||
.add(LootTableReference.lootTableReference(LOOT_TREASURE_DISK))
|
||||
.add(LootTableReference.lootTableReference(TREASURE_DISK_LOOT))
|
||||
.setRolls(ConstantValue.exactly(1));
|
||||
}
|
||||
|
||||
|
@@ -24,12 +24,14 @@ import dan200.computercraft.shared.common.ClearColourRecipe;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
||||
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
|
||||
@@ -97,7 +99,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
@@ -117,7 +119,7 @@ public final class ModRegistry {
|
||||
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
|
||||
|
||||
private static BlockBehaviour.Properties properties() {
|
||||
return BlockBehaviour.Properties.of(Material.STONE).strength(2);
|
||||
return BlockBehaviour.Properties.of().strength(2);
|
||||
}
|
||||
|
||||
private static BlockBehaviour.Properties computerProperties() {
|
||||
@@ -127,45 +129,45 @@ public final class ModRegistry {
|
||||
}
|
||||
|
||||
private static BlockBehaviour.Properties turtleProperties() {
|
||||
return BlockBehaviour.Properties.of(Material.STONE).strength(2.5f);
|
||||
return BlockBehaviour.Properties.of().strength(2.5f);
|
||||
}
|
||||
|
||||
private static BlockBehaviour.Properties modemProperties() {
|
||||
return BlockBehaviour.Properties.of(Material.STONE).strength(1.5f);
|
||||
return BlockBehaviour.Properties.of().strength(1.5f);
|
||||
}
|
||||
|
||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
|
||||
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
|
||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
||||
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
|
||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new ComputerBlock<>(
|
||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(
|
||||
computerProperties().strength(-1, 6000000.0F),
|
||||
ComputerFamily.COMMAND, BlockEntities.COMPUTER_COMMAND
|
||||
));
|
||||
|
||||
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
||||
() -> new TurtleBlock(turtleProperties(), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
|
||||
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
|
||||
() -> new TurtleBlock(turtleProperties(), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
|
||||
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties()));
|
||||
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties()));
|
||||
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties()));
|
||||
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties().mapColor(MapColor.STONE)));
|
||||
|
||||
public static final RegistryEntry<MonitorBlock> MONITOR_NORMAL = REGISTRY.register("monitor_normal",
|
||||
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_NORMAL));
|
||||
() -> new MonitorBlock(properties().mapColor(MapColor.STONE), BlockEntities.MONITOR_NORMAL));
|
||||
public static final RegistryEntry<MonitorBlock> MONITOR_ADVANCED = REGISTRY.register("monitor_advanced",
|
||||
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_ADVANCED));
|
||||
() -> new MonitorBlock(properties().mapColor(MapColor.GOLD), BlockEntities.MONITOR_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_NORMAL = REGISTRY.register("wireless_modem_normal",
|
||||
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_NORMAL));
|
||||
() -> new WirelessModemBlock(properties().mapColor(MapColor.STONE), BlockEntities.WIRELESS_MODEM_NORMAL));
|
||||
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_ADVANCED = REGISTRY.register("wireless_modem_advanced",
|
||||
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_ADVANCED));
|
||||
() -> new WirelessModemBlock(properties().mapColor(MapColor.GOLD), BlockEntities.WIRELESS_MODEM_ADVANCED));
|
||||
|
||||
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
|
||||
() -> new WiredModemFullBlock(modemProperties()));
|
||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties()));
|
||||
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
|
||||
}
|
||||
|
||||
public static class BlockEntities {
|
||||
@@ -222,7 +224,7 @@ public final class ModRegistry {
|
||||
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, ComputerItem::new);
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, ComputerItem::new);
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, ComputerItem::new);
|
||||
public static final RegistryEntry<ComputerItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, CommandComputerItem::new);
|
||||
|
||||
public static final RegistryEntry<PocketComputerItem> POCKET_COMPUTER_NORMAL = REGISTRY.register("pocket_computer_normal",
|
||||
() -> new PocketComputerItem(properties().stacksTo(1), ComputerFamily.NORMAL));
|
||||
@@ -382,51 +384,11 @@ public final class ModRegistry {
|
||||
public static final Predicate<CommandSourceStack> PERMISSION_VIEW = REGISTRY.registerCommand("view", UserLevel.OP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any objects which don't have to be done on the main thread.
|
||||
*/
|
||||
public static void register() {
|
||||
Blocks.REGISTRY.register();
|
||||
BlockEntities.REGISTRY.register();
|
||||
Items.REGISTRY.register();
|
||||
TurtleSerialisers.REGISTRY.register();
|
||||
PocketUpgradeSerialisers.REGISTRY.register();
|
||||
Menus.REGISTRY.register();
|
||||
ArgumentTypes.REGISTRY.register();
|
||||
LootItemConditionTypes.REGISTRY.register();
|
||||
RecipeSerializers.REGISTRY.register();
|
||||
Permissions.REGISTRY.register();
|
||||
static class CreativeTabs {
|
||||
static final RegistrationHelper<CreativeModeTab> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.CREATIVE_MODE_TAB);
|
||||
|
||||
// Register bundled power providers
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
|
||||
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
|
||||
ComputerCraftAPI.registerMediaProvider(stack -> {
|
||||
var item = stack.getItem();
|
||||
if (item instanceof IMedia media) return media;
|
||||
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
|
||||
return null;
|
||||
});
|
||||
|
||||
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
||||
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any objects which must be done on the main thread.
|
||||
*/
|
||||
public static void registerMainThread() {
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link CreativeModeTab.Builder} to contain all of ComputerCraft's items.
|
||||
*
|
||||
* @param builder The builder to configure.
|
||||
* @return The same building, for calling {@link CreativeModeTab.Builder#build()} on.
|
||||
*/
|
||||
public static CreativeModeTab.Builder registerCreativeTab(CreativeModeTab.Builder builder) {
|
||||
return builder
|
||||
@SuppressWarnings("unused")
|
||||
private static final RegistryEntry<CreativeModeTab> TAB = REGISTRY.register("tab", () -> PlatformHelper.get().newCreativeModeTab()
|
||||
.icon(() -> new ItemStack(Items.COMPUTER_NORMAL.get()))
|
||||
.title(Component.translatable("itemGroup.computercraft"))
|
||||
.displayItems((context, out) -> {
|
||||
@@ -458,7 +420,46 @@ public final class ModRegistry {
|
||||
for (var colour = 0; colour < 16; colour++) {
|
||||
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
|
||||
}
|
||||
});
|
||||
})
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any objects which don't have to be done on the main thread.
|
||||
*/
|
||||
public static void register() {
|
||||
Blocks.REGISTRY.register();
|
||||
BlockEntities.REGISTRY.register();
|
||||
Items.REGISTRY.register();
|
||||
TurtleSerialisers.REGISTRY.register();
|
||||
PocketUpgradeSerialisers.REGISTRY.register();
|
||||
Menus.REGISTRY.register();
|
||||
ArgumentTypes.REGISTRY.register();
|
||||
LootItemConditionTypes.REGISTRY.register();
|
||||
RecipeSerializers.REGISTRY.register();
|
||||
Permissions.REGISTRY.register();
|
||||
CreativeTabs.REGISTRY.register();
|
||||
|
||||
// Register bundled power providers
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
|
||||
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
|
||||
ComputerCraftAPI.registerMediaProvider(stack -> {
|
||||
var item = stack.getItem();
|
||||
if (item instanceof IMedia media) return media;
|
||||
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
|
||||
return null;
|
||||
});
|
||||
|
||||
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
||||
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any objects which must be done on the main thread.
|
||||
*/
|
||||
public static void registerMainThread() {
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
|
||||
}
|
||||
|
||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
||||
|
@@ -26,7 +26,6 @@ import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.RelativeMovement;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -34,13 +33,15 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
|
||||
import static dan200.computercraft.shared.command.Exceptions.*;
|
||||
import static dan200.computercraft.shared.command.Exceptions.NOT_TRACKING_EXCEPTION;
|
||||
import static dan200.computercraft.shared.command.Exceptions.NO_TIMINGS_EXCEPTION;
|
||||
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
|
||||
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
|
||||
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.*;
|
||||
@@ -62,114 +63,25 @@ public final class CommandComputerCraft {
|
||||
dispatcher.register(choice("computercraft")
|
||||
.then(literal("dump")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_DUMP)
|
||||
.executes(context -> {
|
||||
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
|
||||
|
||||
var source = context.getSource();
|
||||
List<ServerComputer> computers = new ArrayList<>(ServerContext.get(source.getServer()).registry().getComputers());
|
||||
|
||||
Level world = source.getLevel();
|
||||
var pos = BlockPos.containing(source.getPosition());
|
||||
|
||||
computers.sort((a, b) -> {
|
||||
if (a.getLevel() == b.getLevel() && a.getLevel() == world) {
|
||||
return Double.compare(a.getPosition().distSqr(pos), b.getPosition().distSqr(pos));
|
||||
} else if (a.getLevel() == world) {
|
||||
return -1;
|
||||
} else if (b.getLevel() == world) {
|
||||
return 1;
|
||||
} else {
|
||||
return Integer.compare(a.getInstanceID(), b.getInstanceID());
|
||||
}
|
||||
});
|
||||
|
||||
for (var computer : computers) {
|
||||
table.row(
|
||||
linkComputer(source, computer, computer.getID()),
|
||||
bool(computer.isOn()),
|
||||
linkPosition(source, computer)
|
||||
);
|
||||
}
|
||||
|
||||
table.display(context.getSource());
|
||||
return computers.size();
|
||||
})
|
||||
.executes(c -> dump(c.getSource()))
|
||||
.then(args()
|
||||
.arg("computer", oneComputer())
|
||||
.executes(context -> {
|
||||
var computer = getComputerArgument(context, "computer");
|
||||
|
||||
var table = new TableBuilder("Dump");
|
||||
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
|
||||
table.row(header("Id"), text(Integer.toString(computer.getID())));
|
||||
table.row(header("Label"), text(computer.getLabel()));
|
||||
table.row(header("On"), bool(computer.isOn()));
|
||||
table.row(header("Position"), linkPosition(context.getSource(), computer));
|
||||
table.row(header("Family"), text(computer.getFamily().toString()));
|
||||
|
||||
for (var side : ComputerSide.values()) {
|
||||
var peripheral = computer.getPeripheral(side);
|
||||
if (peripheral != null) {
|
||||
table.row(header("Peripheral " + side.getName()), text(peripheral.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
table.display(context.getSource());
|
||||
return 1;
|
||||
})))
|
||||
.executes(c -> dumpComputer(c.getSource(), getComputerArgument(c, "computer")))))
|
||||
|
||||
.then(command("shutdown")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_SHUTDOWN)
|
||||
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
||||
.executes((context, computerSelectors) -> {
|
||||
var shutdown = 0;
|
||||
var computers = unwrap(context.getSource(), computerSelectors);
|
||||
for (var computer : computers) {
|
||||
if (computer.isOn()) shutdown++;
|
||||
computer.shutdown();
|
||||
}
|
||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.shutdown.done", shutdown, computers.size()), false);
|
||||
return shutdown;
|
||||
}))
|
||||
.executes((c, a) -> shutdown(c.getSource(), unwrap(c.getSource(), a))))
|
||||
|
||||
.then(command("turn-on")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_TURN_ON)
|
||||
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
|
||||
.executes((context, computerSelectors) -> {
|
||||
var on = 0;
|
||||
var computers = unwrap(context.getSource(), computerSelectors);
|
||||
for (var computer : computers) {
|
||||
if (!computer.isOn()) on++;
|
||||
computer.turnOn();
|
||||
}
|
||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.turn_on.done", on, computers.size()), false);
|
||||
return on;
|
||||
}))
|
||||
.executes((c, a) -> turnOn(c.getSource(), unwrap(c.getSource(), a))))
|
||||
|
||||
.then(command("tp")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_TP)
|
||||
.arg("computer", oneComputer())
|
||||
.executes(context -> {
|
||||
var computer = getComputerArgument(context, "computer");
|
||||
var world = computer.getLevel();
|
||||
var pos = computer.getPosition();
|
||||
|
||||
var entity = context.getSource().getEntityOrException();
|
||||
if (!(entity instanceof ServerPlayer player)) throw TP_NOT_PLAYER.create();
|
||||
|
||||
if (player.getCommandSenderWorld() == world) {
|
||||
player.connection.teleport(
|
||||
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0,
|
||||
EnumSet.noneOf(RelativeMovement.class)
|
||||
);
|
||||
} else {
|
||||
player.teleportTo(world,
|
||||
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}))
|
||||
.executes(c -> teleport(c.getSource(), getComputerArgument(c, "computer"))))
|
||||
|
||||
.then(command("queue")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_QUEUE)
|
||||
@@ -178,79 +90,243 @@ public final class CommandComputerCraft {
|
||||
.suggests((context, builder) -> Suggestions.empty())
|
||||
)
|
||||
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
|
||||
.executes((ctx, args) -> {
|
||||
var computers = getComputersArgument(ctx, "computer");
|
||||
var rest = args.toArray();
|
||||
|
||||
var queued = 0;
|
||||
for (var computer : computers) {
|
||||
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) {
|
||||
computer.queueEvent("computer_command", rest);
|
||||
queued++;
|
||||
}
|
||||
}
|
||||
|
||||
return queued;
|
||||
}))
|
||||
.executes((c, a) -> queue(getComputersArgument(c, "computer"), a)))
|
||||
|
||||
.then(command("view")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_VIEW)
|
||||
.arg("computer", oneComputer())
|
||||
.executes(context -> {
|
||||
var player = context.getSource().getPlayerOrException();
|
||||
var computer = getComputerArgument(context, "computer");
|
||||
new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() {
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return Component.translatable("gui.computercraft.view_computer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
|
||||
return new ViewComputerMenu(id, player, computer);
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
}))
|
||||
.executes(c -> view(c.getSource(), getComputerArgument(c, "computer"))))
|
||||
|
||||
.then(choice("track")
|
||||
.requires(ModRegistry.Permissions.PERMISSION_TRACK)
|
||||
.then(command("start")
|
||||
.executes(context -> {
|
||||
getMetricsInstance(context.getSource()).start();
|
||||
|
||||
var stopCommand = "/computercraft track stop";
|
||||
context.getSource().sendSuccess(Component.translatable(
|
||||
"commands.computercraft.track.start.stop",
|
||||
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
||||
), false);
|
||||
return 1;
|
||||
}))
|
||||
|
||||
.then(command("stop")
|
||||
.executes(context -> {
|
||||
var timings = getMetricsInstance(context.getSource());
|
||||
if (!timings.stop()) throw NOT_TRACKING_EXCEPTION.create();
|
||||
displayTimings(context.getSource(), timings.getSnapshot(), new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG), DEFAULT_FIELDS);
|
||||
return 1;
|
||||
}))
|
||||
|
||||
.then(command("start").executes(c -> trackStart(c.getSource())))
|
||||
.then(command("stop").executes(c -> trackStop(c.getSource())))
|
||||
.then(command("dump")
|
||||
.argManyValue("fields", metric(), DEFAULT_FIELDS)
|
||||
.executes((context, fields) -> {
|
||||
AggregatedMetric sort;
|
||||
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get(0))) {
|
||||
sort = fields.get(0);
|
||||
fields = DEFAULT_FIELDS;
|
||||
} else {
|
||||
sort = fields.get(0);
|
||||
}
|
||||
|
||||
return displayTimings(context.getSource(), sort, fields);
|
||||
})))
|
||||
.executes((c, f) -> trackDump(c.getSource(), f))))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display loaded computers to a table.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @return The number of loaded computers.
|
||||
*/
|
||||
private static int dump(CommandSourceStack source) {
|
||||
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
|
||||
|
||||
List<ServerComputer> computers = new ArrayList<>(ServerContext.get(source.getServer()).registry().getComputers());
|
||||
|
||||
Level world = source.getLevel();
|
||||
var pos = BlockPos.containing(source.getPosition());
|
||||
|
||||
// Sort by nearby computers.
|
||||
computers.sort((a, b) -> {
|
||||
if (a.getLevel() == b.getLevel() && a.getLevel() == world) {
|
||||
return Double.compare(a.getPosition().distSqr(pos), b.getPosition().distSqr(pos));
|
||||
} else if (a.getLevel() == world) {
|
||||
return -1;
|
||||
} else if (b.getLevel() == world) {
|
||||
return 1;
|
||||
} else {
|
||||
return Integer.compare(a.getInstanceID(), b.getInstanceID());
|
||||
}
|
||||
});
|
||||
|
||||
for (var computer : computers) {
|
||||
table.row(
|
||||
linkComputer(source, computer, computer.getID()),
|
||||
bool(computer.isOn()),
|
||||
linkPosition(source, computer)
|
||||
);
|
||||
}
|
||||
|
||||
table.display(source);
|
||||
return computers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display additional information about a single computer.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @param computer The computer we're dumping.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int dumpComputer(CommandSourceStack source, ServerComputer computer) {
|
||||
var table = new TableBuilder("Dump");
|
||||
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
|
||||
table.row(header("Id"), text(Integer.toString(computer.getID())));
|
||||
table.row(header("Label"), text(computer.getLabel()));
|
||||
table.row(header("On"), bool(computer.isOn()));
|
||||
table.row(header("Position"), linkPosition(source, computer));
|
||||
table.row(header("Family"), text(computer.getFamily().toString()));
|
||||
|
||||
for (var side : ComputerSide.values()) {
|
||||
var peripheral = computer.getPeripheral(side);
|
||||
if (peripheral != null) {
|
||||
table.row(header("Peripheral " + side.getName()), text(peripheral.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
table.display(source);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown a list of computers.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @param computers The computers to shutdown.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int shutdown(CommandSourceStack source, Collection<ServerComputer> computers) {
|
||||
var shutdown = 0;
|
||||
for (var computer : computers) {
|
||||
if (computer.isOn()) shutdown++;
|
||||
computer.shutdown();
|
||||
}
|
||||
|
||||
var didShutdown = shutdown;
|
||||
source.sendSuccess(() -> Component.translatable("commands.computercraft.shutdown.done", didShutdown, computers.size()), false);
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on a list of computers.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @param computers The computers to turn on.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int turnOn(CommandSourceStack source, Collection<ServerComputer> computers) {
|
||||
var on = 0;
|
||||
for (var computer : computers) {
|
||||
if (!computer.isOn()) on++;
|
||||
computer.turnOn();
|
||||
}
|
||||
|
||||
var didOn = on;
|
||||
source.sendSuccess(() -> Component.translatable("commands.computercraft.turn_on.done", didOn, computers.size()), false);
|
||||
return on;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleport to a computer.
|
||||
*
|
||||
* @param source The thing that executed this command. This must be an entity, other types will throw an exception.
|
||||
* @param computer The computer to teleport to.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int teleport(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
||||
var world = computer.getLevel();
|
||||
var pos = Vec3.atBottomCenterOf(computer.getPosition());
|
||||
source.getEntityOrException().teleportTo(world, pos.x(), pos.y(), pos.z(), EnumSet.noneOf(RelativeMovement.class), 0, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a {@code computer_command} event on a command computer.
|
||||
*
|
||||
* @param computers The list of computers to queue on.
|
||||
* @param args The arguments for this event.
|
||||
* @return The number of computers this event was queued on.
|
||||
*/
|
||||
private static int queue(Collection<ServerComputer> computers, List<String> args) {
|
||||
var rest = args.toArray();
|
||||
|
||||
var queued = 0;
|
||||
for (var computer : computers) {
|
||||
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) {
|
||||
computer.queueEvent("computer_command", rest);
|
||||
queued++;
|
||||
}
|
||||
}
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a terminal for a computer.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @param computer The computer to view.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
||||
var player = source.getPlayerOrException();
|
||||
new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() {
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return Component.translatable("gui.computercraft.view_computer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
|
||||
return new ViewComputerMenu(id, player, computer);
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking metrics for the current player.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int trackStart(CommandSourceStack source) {
|
||||
getMetricsInstance(source).start();
|
||||
|
||||
var stopCommand = "/computercraft track stop";
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
"commands.computercraft.track.start.stop",
|
||||
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
||||
), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop tracking metrics for the current player, displaying a table with the results.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int trackStop(CommandSourceStack source) throws CommandSyntaxException {
|
||||
var metrics = getMetricsInstance(source);
|
||||
if (!metrics.stop()) throw NOT_TRACKING_EXCEPTION.create();
|
||||
displayTimings(source, metrics.getSnapshot(), new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG), DEFAULT_FIELDS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)
|
||||
);
|
||||
|
||||
/**
|
||||
* Display the latest metrics for the current player.
|
||||
*
|
||||
* @param source The thing that executed this command.
|
||||
* @param fields The fields to display in this table, defaulting to {@link #DEFAULT_FIELDS}.
|
||||
* @return The constant {@code 1}.
|
||||
*/
|
||||
private static int trackDump(CommandSourceStack source, List<AggregatedMetric> fields) throws CommandSyntaxException {
|
||||
AggregatedMetric sort;
|
||||
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get(0))) {
|
||||
sort = fields.get(0);
|
||||
fields = DEFAULT_FIELDS;
|
||||
} else {
|
||||
sort = fields.get(0);
|
||||
}
|
||||
|
||||
return displayTimings(source, getMetricsInstance(source).getTimings(), sort, fields);
|
||||
}
|
||||
|
||||
// Additional helper functions.
|
||||
|
||||
private static Component linkComputer(CommandSourceStack source, @Nullable ServerComputer serverComputer, int computerId) {
|
||||
var out = Component.literal("");
|
||||
|
||||
@@ -323,16 +399,6 @@ public final class CommandComputerCraft {
|
||||
return ServerContext.get(source.getServer()).metrics().getMetricsInstance(entity instanceof Player ? entity.getUUID() : SYSTEM_UUID);
|
||||
}
|
||||
|
||||
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
|
||||
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)
|
||||
);
|
||||
|
||||
private static int displayTimings(CommandSourceStack source, AggregatedMetric sortField, List<AggregatedMetric> fields) throws CommandSyntaxException {
|
||||
return displayTimings(source, getMetricsInstance(source).getTimings(), sortField, fields);
|
||||
}
|
||||
|
||||
private static int displayTimings(CommandSourceStack source, List<ComputerMetrics> timings, AggregatedMetric sortField, List<AggregatedMetric> fields) throws CommandSyntaxException {
|
||||
if (timings.isEmpty()) throw NO_TIMINGS_EXCEPTION.create();
|
||||
|
||||
|
@@ -18,9 +18,6 @@ public final class Exceptions {
|
||||
static final SimpleCommandExceptionType NOT_TRACKING_EXCEPTION = translated("commands.computercraft.track.stop.not_enabled");
|
||||
static final SimpleCommandExceptionType NO_TIMINGS_EXCEPTION = translated("commands.computercraft.track.dump.no_timings");
|
||||
|
||||
static final SimpleCommandExceptionType TP_NOT_THERE = translated("commands.computercraft.tp.not_there");
|
||||
static final SimpleCommandExceptionType TP_NOT_PLAYER = translated("commands.computercraft.tp.not_player");
|
||||
|
||||
public static final SimpleCommandExceptionType ARGUMENT_EXPECTED = translated("argument.computercraft.argument_expected");
|
||||
|
||||
private static SimpleCommandExceptionType translated(String key) {
|
||||
|
@@ -147,14 +147,14 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
|
||||
@Override
|
||||
public int run(CommandContext<CommandSourceStack> context) {
|
||||
context.getSource().sendSuccess(getHelp(context, assertNonNull(node), id, command), false);
|
||||
context.getSource().sendSuccess(() -> getHelp(context, assertNonNull(node), id, command), false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Command<CommandSourceStack> helpForChild(CommandNode<CommandSourceStack> node, String id, String command) {
|
||||
return context -> {
|
||||
context.getSource().sendSuccess(getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
|
||||
context.getSource().sendSuccess(() -> getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
@@ -37,6 +37,6 @@ public class ServerTableFormatter implements TableFormatter {
|
||||
|
||||
@Override
|
||||
public void writeLine(String label, Component component) {
|
||||
source.sendSuccess(component, false);
|
||||
source.sendSuccess(() -> component, false);
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,6 @@ public class DefaultBundledRedstoneProvider implements BundledRedstoneProvider {
|
||||
|
||||
public static int getDefaultBundledRedstoneOutput(Level world, BlockPos pos, Direction side) {
|
||||
var block = world.getBlockState(pos).getBlock();
|
||||
if (block instanceof IBundledRedstoneBlock generic) {
|
||||
if (generic.getBundledRedstoneConnectivity(world, pos, side)) {
|
||||
return generic.getBundledRedstoneOutput(world, pos, side);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return block instanceof IBundledRedstoneBlock bundledBlock ? bundledBlock.getBundledRedstoneOutput(world, pos, side) : -1;
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,5 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public interface IBundledRedstoneBlock {
|
||||
boolean getBundledRedstoneConnectivity(Level world, BlockPos pos, Direction side);
|
||||
|
||||
int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.storage.loot.LootContext;
|
||||
import net.minecraft.world.level.storage.loot.LootParams;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -92,11 +92,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
return getDirectSignal(state, world, pos, incomingSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBundledRedstoneConnectivity(Level world, BlockPos pos, Direction side) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side) {
|
||||
var entity = world.getBlockEntity(pos);
|
||||
@@ -138,13 +133,12 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof AbstractComputerBlockEntity computer) {
|
||||
var context = new LootContext.Builder(serverWorld)
|
||||
.withRandom(world.random)
|
||||
var context = new LootParams.Builder(serverWorld)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
|
||||
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
|
||||
.withParameter(LootContextParams.THIS_ENTITY, player)
|
||||
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
|
||||
.withDynamicDrop(DROP, (ctx, out) -> out.accept(getItem(computer)));
|
||||
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
|
||||
for (var item : state.getDrops(context)) {
|
||||
popResource(world, pos, item);
|
||||
}
|
||||
|
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.computer.blocks;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||
import net.minecraft.world.level.block.GameMasterBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
|
||||
/**
|
||||
* A subclass of {@link ComputerBlock} which implements {@link GameMasterBlock}, to prevent players breaking it without
|
||||
* permission.
|
||||
*
|
||||
* @param <T> The type of the computer block entity.
|
||||
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
|
||||
*/
|
||||
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
|
||||
public CommandComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
|
||||
super(settings, family, type);
|
||||
}
|
||||
}
|
@@ -104,11 +104,15 @@ public class CommandComputerBlockEntity extends ComputerBlockEntity {
|
||||
if (server == null || !server.isCommandBlockEnabled()) {
|
||||
player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
|
||||
return false;
|
||||
} else if (Config.commandRequireCreative ? !player.canUseGameMasterBlocks() : !server.getPlayerList().isOp(player.getGameProfile())) {
|
||||
} else if (!canUseCommandBlock(player)) {
|
||||
player.displayClientMessage(Component.translatable("advMode.notAllowed"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean canUseCommandBlock(Player player) {
|
||||
return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2);
|
||||
}
|
||||
}
|
||||
|
@@ -64,13 +64,7 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute
|
||||
|
||||
@Override
|
||||
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||
var family = getFamily();
|
||||
if (family != ComputerFamily.COMMAND) {
|
||||
var id = getComputerID(stack);
|
||||
if (id >= 0) {
|
||||
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
var id = getComputerID(stack);
|
||||
return id >= 0 ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit) : null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.computer.items;
|
||||
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ComputerItem} which prevents players placing it without permission.
|
||||
*
|
||||
* @see net.minecraft.world.item.GameMasterBlockItem
|
||||
* @see dan200.computercraft.shared.computer.blocks.CommandComputerBlock
|
||||
*/
|
||||
public class CommandComputerItem extends ComputerItem {
|
||||
public CommandComputerItem(ComputerBlock<?> block, Properties settings) {
|
||||
super(block, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable BlockState getPlacementState(BlockPlaceContext context) {
|
||||
// Prohibit players placing this block in survival or when not opped.
|
||||
var player = context.getPlayer();
|
||||
return player != null && !player.canUseGameMasterBlocks() ? null : super.getPlacementState(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||
// Don't allow command computers to be mounted in disk drives.
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -141,7 +141,7 @@ public final class ComputerMBean implements DynamicMBean, ComputerMetricsObserve
|
||||
}
|
||||
}
|
||||
|
||||
private static class Counter {
|
||||
private static final class Counter {
|
||||
final AtomicLong value = new AtomicLong();
|
||||
final AtomicLong count = new AtomicLong();
|
||||
}
|
||||
|
@@ -116,6 +116,7 @@ public class ItemDetails {
|
||||
* @param enchants The enchantment map to add it to.
|
||||
* @see EnchantmentHelper
|
||||
*/
|
||||
@SuppressWarnings("NonApiType")
|
||||
private static void addEnchantments(ListTag rawEnchants, ArrayList<Map<String, Object>> enchants) {
|
||||
if (rawEnchants.isEmpty()) return;
|
||||
|
||||
|
@@ -69,7 +69,7 @@ public abstract class PermissionRegistry {
|
||||
.orElseGet(DefaultPermissionRegistry::new);
|
||||
}
|
||||
|
||||
private static class DefaultPermissionRegistry extends PermissionRegistry {
|
||||
private static final class DefaultPermissionRegistry extends PermissionRegistry {
|
||||
@Override
|
||||
public Predicate<CommandSourceStack> registerCommand(String command, UserLevel fallback) {
|
||||
checkNotFrozen();
|
||||
|
@@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
||||
private static final String NBT_ITEM = "Item";
|
||||
|
||||
private static class MountInfo {
|
||||
private static final class MountInfo {
|
||||
@Nullable
|
||||
String mountPath;
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ import java.util.Collections;
|
||||
public class CableBlockEntity extends BlockEntity {
|
||||
private static final String NBT_PERIPHERAL_ENABLED = "PeripheralAccess";
|
||||
|
||||
private class CableElement extends WiredModemElement {
|
||||
private final class CableElement extends WiredModemElement {
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
return CableBlockEntity.this.getLevel();
|
||||
|
@@ -19,7 +19,7 @@ public record SpeakerPosition(@Nullable Level level, Vec3 position, @Nullable En
|
||||
}
|
||||
|
||||
public static SpeakerPosition of(Entity entity) {
|
||||
return new SpeakerPosition(entity.level, entity.getEyePosition(1), entity);
|
||||
return new SpeakerPosition(entity.level(), entity.getEyePosition(1), entity);
|
||||
}
|
||||
|
||||
public boolean withinDistance(SpeakerPosition other, double distanceSq) {
|
||||
|
@@ -33,6 +33,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -279,6 +280,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
*/
|
||||
int getBurnTime(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Create a builder for a new creative tab.
|
||||
*
|
||||
* @return The creative tab builder.
|
||||
*/
|
||||
CreativeModeTab.Builder newCreativeModeTab();
|
||||
|
||||
/**
|
||||
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return
|
||||
* an empty bucket.
|
||||
|
@@ -153,7 +153,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
super.tickServer();
|
||||
|
||||
// Find any players which have gone missing and remove them from the tracking list.
|
||||
tracking.removeIf(player -> !player.isAlive() || player.level != getLevel());
|
||||
tracking.removeIf(player -> !player.isAlive() || player.level() != getLevel());
|
||||
|
||||
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
||||
var sendState = hasOutputChanged() || lightChanged;
|
||||
|
@@ -48,7 +48,7 @@ public class PocketComputerMenuProvider implements MenuProvider {
|
||||
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.POCKET_COMPUTER.get(), id, inventory,
|
||||
p -> {
|
||||
var stack = p.getItemInHand(hand);
|
||||
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level.getServer()), stack) == computer;
|
||||
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer;
|
||||
},
|
||||
computer, item.getFamily()
|
||||
);
|
||||
|
@@ -127,10 +127,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
|
||||
@ForgeOverride
|
||||
public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) {
|
||||
if (entity.level.isClientSide || entity.level.getServer() == null) return false;
|
||||
var level = entity.level();
|
||||
if (level.isClientSide || level.getServer() == null) return false;
|
||||
|
||||
var computer = getServerComputer(entity.level.getServer(), stack);
|
||||
if (computer != null && tick(stack, entity.level, entity, computer)) entity.setItem(stack.copy());
|
||||
var computer = getServerComputer(level.getServer(), stack);
|
||||
if (computer != null && tick(stack, entity.level(), entity, computer)) entity.setItem(stack.copy());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||
public void update() {
|
||||
var entity = access.getEntity();
|
||||
if (entity != null) {
|
||||
level = entity.level;
|
||||
level = entity.level();
|
||||
position = entity.position();
|
||||
}
|
||||
|
||||
|
@@ -317,7 +317,6 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getVisualPosition(float f) {
|
||||
var offset = getRenderOffset(f);
|
||||
var pos = owner.getBlockPos();
|
||||
@@ -328,7 +327,6 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVisualYaw(float f) {
|
||||
var yaw = getDirection().toYRot();
|
||||
switch (animation) {
|
||||
|
@@ -44,7 +44,7 @@ public class TurtleMoveCommand implements TurtleCommand {
|
||||
var state = oldWorld.getBlockState(newPosition);
|
||||
if (!oldWorld.isEmptyBlock(newPosition) &&
|
||||
!WorldUtil.isLiquidBlock(oldWorld, newPosition) &&
|
||||
!state.getMaterial().isReplaceable()) {
|
||||
!state.canBeReplaced()) {
|
||||
return TurtleCommandResult.failure("Movement obstructed");
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,7 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.SignText;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
@@ -208,7 +209,7 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
);
|
||||
if (result != InteractionResult.PASS) return result;
|
||||
|
||||
var level = turtlePlayer.player().level;
|
||||
var level = turtlePlayer.player().level();
|
||||
|
||||
// We special case some items which we allow to place "normally". Yes, this is very ugly.
|
||||
var item = stack.getItem();
|
||||
@@ -223,22 +224,22 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
var signTile = (SignBlockEntity) tile;
|
||||
var split = Splitter.on('\n').splitToList(message);
|
||||
var firstLine = split.size() <= 2 ? 1 : 0;
|
||||
|
||||
var signText = new SignText();
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if (i >= firstLine && i < firstLine + split.size()) {
|
||||
var line = split.get(i - firstLine);
|
||||
signTile.setMessage(i, line.length() > 15
|
||||
signText.setMessage(i, line.length() > 15
|
||||
? Component.literal(line.substring(0, 15))
|
||||
: Component.literal(line)
|
||||
);
|
||||
} else {
|
||||
signTile.setMessage(i, Component.literal(""));
|
||||
}
|
||||
}
|
||||
signTile.setChanged();
|
||||
signTile.setText(signText, true);
|
||||
world.sendBlockUpdated(tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
|
||||
private static class ErrorMessage {
|
||||
private static final class ErrorMessage {
|
||||
@Nullable
|
||||
String message;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
@@ -17,20 +18,22 @@ import net.minecraft.world.item.crafting.RecipeType;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TurtleInventoryCrafting extends CraftingContainer {
|
||||
public class TurtleInventoryCrafting implements CraftingContainer {
|
||||
public static final int WIDTH = 3;
|
||||
public static final int HEIGHT = 3;
|
||||
public static final int SIZE = WIDTH * HEIGHT;
|
||||
|
||||
private final ITurtleAccess turtle;
|
||||
private int xStart = 0;
|
||||
private int yStart = 0;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public TurtleInventoryCrafting(ITurtleAccess turtle) {
|
||||
// Passing null in here is evil, but we don't have a container present. We override most methods in order to
|
||||
// avoid throwing any NPEs.
|
||||
super(null, 0, 0);
|
||||
this.turtle = turtle;
|
||||
}
|
||||
|
||||
@@ -96,7 +99,7 @@ public class TurtleInventoryCrafting extends CraftingContainer {
|
||||
// afterwards).
|
||||
if (existing.isEmpty()) {
|
||||
setItem(slot, remainder);
|
||||
} else if (ItemStack.isSame(existing, remainder) && ItemStack.tagMatches(existing, remainder)) {
|
||||
} else if (ItemStack.isSameItemSameTags(existing, remainder)) {
|
||||
remainder.grow(existing.getCount());
|
||||
setItem(slot, remainder);
|
||||
} else {
|
||||
@@ -110,12 +113,12 @@ public class TurtleInventoryCrafting extends CraftingContainer {
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 3;
|
||||
return WIDTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 3;
|
||||
return HEIGHT;
|
||||
}
|
||||
|
||||
private int modifyIndex(int index) {
|
||||
@@ -126,35 +129,37 @@ public class TurtleInventoryCrafting extends CraftingContainer {
|
||||
: -1;
|
||||
}
|
||||
|
||||
// IInventory implementation
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
if (!getItem(i).isEmpty()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return getWidth() * getHeight();
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int i) {
|
||||
i = modifyIndex(i);
|
||||
return turtle.getInventory().getItem(i);
|
||||
return turtle.getInventory().getItem(modifyIndex(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int i) {
|
||||
i = modifyIndex(i);
|
||||
return turtle.getInventory().removeItemNoUpdate(i);
|
||||
return turtle.getInventory().removeItemNoUpdate(modifyIndex(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int i, int size) {
|
||||
i = modifyIndex(i);
|
||||
return turtle.getInventory().removeItem(i, size);
|
||||
return turtle.getInventory().removeItem(modifyIndex(i), size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int i, ItemStack stack) {
|
||||
i = modifyIndex(i);
|
||||
turtle.getInventory().setItem(i, stack);
|
||||
turtle.getInventory().setItem(modifyIndex(i), stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,15 +179,34 @@ public class TurtleInventoryCrafting extends CraftingContainer {
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int i, ItemStack stack) {
|
||||
i = modifyIndex(i);
|
||||
return turtle.getInventory().canPlaceItem(i, stack);
|
||||
return turtle.getInventory().canPlaceItem(modifyIndex(i), stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for (var i = 0; i < getContainerSize(); i++) {
|
||||
for (var i = 0; i < SIZE; i++) {
|
||||
var j = modifyIndex(i);
|
||||
turtle.getInventory().setItem(j, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedContents contents) {
|
||||
for (int i = 0; i < SIZE; i++) contents.accountSimpleStack(getItem(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getItems() {
|
||||
return new AbstractList<>() {
|
||||
@Override
|
||||
public ItemStack get(int index) {
|
||||
return getItem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ public final class DropConsumer {
|
||||
dropConsumer = consumer;
|
||||
remainingDrops = new ArrayList<>();
|
||||
dropEntity = entity;
|
||||
dropWorld = entity.level;
|
||||
dropWorld = entity.level();
|
||||
dropBounds = new AABB(entity.blockPosition()).inflate(2, 2, 2);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public final class DropConsumer {
|
||||
|
||||
public static boolean onEntitySpawn(Entity entity) {
|
||||
// Capture any nearby item spawns
|
||||
if (dropWorld == entity.getLevel() && entity instanceof ItemEntity
|
||||
if (dropWorld == entity.level() && entity instanceof ItemEntity
|
||||
&& assertNonNull(dropBounds).contains(entity.position())) {
|
||||
handleDrops(((ItemEntity) entity).getItem());
|
||||
return true;
|
||||
|
@@ -90,6 +90,6 @@ public final class InventoryUtil {
|
||||
if (stack1.getItem() != stack2.getItem()) return false;
|
||||
if (stack1.getDamageValue() != stack2.getDamageValue()) return false;
|
||||
if (stack1.getCount() > stack1.getMaxStackSize()) return false;
|
||||
return ItemStack.tagMatches(stack1, stack2);
|
||||
return ItemStack.isSameItemSameTags(stack1, stack2);
|
||||
}
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ public final class WorldUtil {
|
||||
|
||||
public static boolean isLiquidBlock(Level world, BlockPos pos) {
|
||||
if (!world.isInWorldBounds(pos)) return false;
|
||||
return world.getBlockState(pos).getMaterial().isLiquid();
|
||||
return world.getBlockState(pos).liquid();
|
||||
}
|
||||
|
||||
public static boolean isVecInside(VoxelShape shape, Vec3 vec) {
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Různé příkazy pro ovládání počítačů.",
|
||||
"commands.computercraft.tp.action": "Teleportovat se k počítači",
|
||||
"commands.computercraft.tp.desc": "Teleportovat se na místo počítače. Můžeš specifikovat ID počítačové instance (tř. 123) nebo ID počítače (tř. #123).",
|
||||
"commands.computercraft.tp.not_player": "Nelze otevřít terminál pro nehráče",
|
||||
"commands.computercraft.tp.not_there": "Nelze najít počítač ve světě",
|
||||
"commands.computercraft.tp.synopsis": "Teleportovat se ke specifickému počítači.",
|
||||
"commands.computercraft.track.desc": "Sledovat jak dlouho se počítače spustí, a také kolik událostí zpracují. Toto uvádí informace v podobné cestě jako /forge track a může být dobré pro diagnostiku lagu.",
|
||||
"commands.computercraft.track.dump.computer": "Počítač",
|
||||
|
@@ -46,8 +46,6 @@
|
||||
"commands.computercraft.synopsis": "Verschiedene Befehle um Computer zu kontrollieren.",
|
||||
"commands.computercraft.tp.action": "Teleportiert dich zum Computer",
|
||||
"commands.computercraft.tp.desc": "Teleportiert dich zum Standort eines Computers. Der Computer kann entweder über seine Instanz ID (z.B. 123), seine Computer ID (z.B. #123) oder seinen Namen (z.B. \"@Mein Computer\") angegeben werden.",
|
||||
"commands.computercraft.tp.not_player": "Konnte Terminal für Nicht-Spieler nicht öffnen",
|
||||
"commands.computercraft.tp.not_there": "Konnte Computer in der Welt nicht finden",
|
||||
"commands.computercraft.tp.synopsis": "Teleportiert dich zum angegebenen Computer.",
|
||||
"commands.computercraft.track.desc": "Zeichnet die Laufzeiten von Computern und wie viele Events ausgelöst werden auf. Die Ausgabe der Informationen ist ähnlich zu /forge track, was beim aufspüren von Lags sehr hilfreich sein kann.",
|
||||
"commands.computercraft.track.dump.computer": "Computer",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Commandes diverses pour contrôler les ordinateurs.",
|
||||
"commands.computercraft.tp.action": "Se téléporter vers cet ordinateur",
|
||||
"commands.computercraft.tp.desc": "Se téléporter à la position de l'ordinateur. Vous pouvez spécifier l'identifiant d'instance (ex. 123) ou l'identifiant d'ordinateur (ex. #123).",
|
||||
"commands.computercraft.tp.not_player": "Impossible d'ouvrir un terminal pour un non-joueur",
|
||||
"commands.computercraft.tp.not_there": "Impossible de localiser cet ordinateur dans le monde",
|
||||
"commands.computercraft.tp.synopsis": "Se téléporter à la position de l'ordinateur spécifié.",
|
||||
"commands.computercraft.track.desc": "Surveillez combien de temps prend une exécutions sur les ordinateurs, ainsi que le nombre d’événements capturés. Les informations sont affichées d'une manière similaire à la commande /forge track, utile pour diagnostiquer les sources de latence.",
|
||||
"commands.computercraft.track.dump.computer": "Ordinateur",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Vari comandi per controllare i computer.",
|
||||
"commands.computercraft.tp.action": "Teletrasporta a questo computer",
|
||||
"commands.computercraft.tp.desc": "Teletrasporta alla posizione di un computer. Puoi specificare il computer con l'instance id (e.g. 123) o con l'id (e.g. #123).",
|
||||
"commands.computercraft.tp.not_player": "Non è possibile aprire un terminale per un non giocatore",
|
||||
"commands.computercraft.tp.not_there": "Impossibile trovare il computer nel mondo",
|
||||
"commands.computercraft.tp.synopsis": "Teletrasporta al computer specificato.",
|
||||
"commands.computercraft.track.desc": "Monitora per quanto tempo i computer vengono eseguiti e quanti eventi ricevono. Questo comando fornisce le informazioni in modo simile a /forge track e può essere utile per diagnosticare il lag.",
|
||||
"commands.computercraft.track.dump.computer": "Computer",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
|
||||
"commands.computercraft.tp.action": "このコンピューターへテレポートします",
|
||||
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。",
|
||||
"commands.computercraft.tp.not_player": "非プレイヤー用のターミナルを開くことができません",
|
||||
"commands.computercraft.tp.not_there": "世界でコンピュータを見つけることができませんでした",
|
||||
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
|
||||
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
|
||||
"commands.computercraft.track.dump.computer": "コンピューター",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "컴퓨터를 제어하기 위한 다양한 명령어",
|
||||
"commands.computercraft.tp.action": "이 컴퓨터로 순간이동하기",
|
||||
"commands.computercraft.tp.desc": "컴퓨터의 위치로 순간이동합니다. 컴퓨터의 인스턴스 ID(예: 123) 또는 컴퓨터 ID(예: #123)를 지정할 수 있습니다.",
|
||||
"commands.computercraft.tp.not_player": "비플레이어용 터미널을 열 수 없습니다.",
|
||||
"commands.computercraft.tp.not_there": "월드에서 컴퓨터를 위치시킬 수 없습니다.",
|
||||
"commands.computercraft.tp.synopsis": "특정 컴퓨터로 순간이동하기",
|
||||
"commands.computercraft.track.desc": "컴퓨터가 실행되는 기간과 처리되는 이벤트 수를 추적합니다. 이는 /forge 트랙과 유사한 방법으로 정보를 제공하며 지연 로그에 유용할 수 있습니다.",
|
||||
"commands.computercraft.track.dump.computer": "컴퓨터",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Forskjellige kommandoer for å kontrollere datamaskiner.",
|
||||
"commands.computercraft.tp.action": "Teleporter til denne datamaskinen",
|
||||
"commands.computercraft.tp.desc": "Teleporter til en datamaskin sin posisjon. Du kan enten spesifisere datamaskinens forekomst id (f. eks 123) eller datamaskinens id (f. eks #123).",
|
||||
"commands.computercraft.tp.not_player": "Kan kun åpne terminalen for spillere",
|
||||
"commands.computercraft.tp.not_there": "Greide ikke å finne datamaskinen i denne verdenen",
|
||||
"commands.computercraft.tp.synopsis": "Teleporter til en spesifikk datamaskin.",
|
||||
"commands.computercraft.track.desc": "Spor hvor lenge datamaskiner kjører, samt hvor mange hendelser de handler. Dette presenterer informasjon i en lignende vei til /forge track og kan være nyttig for å diagnostisere lagg.",
|
||||
"commands.computercraft.track.dump.computer": "Datamaskin",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Verschillende commando's voor het beheren van computers.",
|
||||
"commands.computercraft.tp.action": "Teleporteer naar deze computer",
|
||||
"commands.computercraft.tp.desc": "Teleporteer naar de locatie van een specefieke computer. Je kunt een instance-id (bijv. 123) of computer-id (bijv. #123) opgeven.",
|
||||
"commands.computercraft.tp.not_player": "Kan geen terminal openen voor non-speler",
|
||||
"commands.computercraft.tp.not_there": "Kan de computer niet lokaliseren",
|
||||
"commands.computercraft.tp.synopsis": "Teleporteer naar een specifieke computer.",
|
||||
"commands.computercraft.track.desc": "Houd uitvoertijd en het aantal behandelde events van computers bij. Dit biedt informatie op een gelijke manier als /forge track en kan nuttig zijn in het opsloren van lag.",
|
||||
"commands.computercraft.track.dump.computer": "Computer",
|
||||
|
@@ -31,7 +31,6 @@
|
||||
"commands.computercraft.synopsis": "Różne komendy do kontrolowania komputerami.",
|
||||
"commands.computercraft.tp.action": "Przeteleportuj się do podanego komputera",
|
||||
"commands.computercraft.tp.desc": "Przeteleportuj się do lokalizacji komputera. Możesz wybrać numer sesji komputera (np. 123) lub ID komputera (np. #123).",
|
||||
"commands.computercraft.tp.not_there": "Nie można zlokalizować komputera w świecie",
|
||||
"commands.computercraft.tp.synopsis": "Przeteleportuj się do podanego komputera.",
|
||||
"commands.computercraft.track.dump.computer": "Komputer",
|
||||
"commands.computercraft.turn_on.desc": "Włącz podane komputery. Możesz wybrać numer sesji komputera (np. 123), ID komputera (np. #123) lub jego etykietę (np. \"@Mój Komputer\").",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Различные команды для управления компьютерами.",
|
||||
"commands.computercraft.tp.action": "Телепортироваться к этому компьютеру",
|
||||
"commands.computercraft.tp.desc": "Телепортироваться к местоположению компьютера. Ты можешь указать либо идентификатор экземпляра компьютера (например 123) либо идентификатор компьютера (например #123).",
|
||||
"commands.computercraft.tp.not_player": "Нельзя открыть терминал для не-игрока",
|
||||
"commands.computercraft.tp.not_there": "Нельзя определить в мире местоположение компьютер",
|
||||
"commands.computercraft.tp.synopsis": "Телепортироваться к конкретному компьютеру.",
|
||||
"commands.computercraft.track.desc": "Отслеживает, как долго компьютеры исполняют, а также то, как много они обрабатывают события. Эта информация представляется аналогично к /forge track и может быть полезной для диагностики лага.",
|
||||
"commands.computercraft.track.dump.computer": "Компьютер",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Olika kommandon för att kontrollera datorer.",
|
||||
"commands.computercraft.tp.action": "Teleportera till den här datorn",
|
||||
"commands.computercraft.tp.desc": "Teleportera till datorns position. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").",
|
||||
"commands.computercraft.tp.not_player": "Kan inte öppna terminalen för en ickespelare",
|
||||
"commands.computercraft.tp.not_there": "Kan inte hitta datorn i världen",
|
||||
"commands.computercraft.tp.synopsis": "Teleportera till en specifik dator.",
|
||||
"commands.computercraft.track.desc": "Spåra hur länge datorer exekverar, och även hur många event de hanterar. Detta presenterar information på liknande sätt som /forge track och kan vara användbart för att undersöka lagg.",
|
||||
"commands.computercraft.track.dump.computer": "Dator",
|
||||
|
@@ -44,8 +44,6 @@
|
||||
"commands.computercraft.synopsis": "mi jo e toki wawa ante tawa ni: sina lawa e ilo sona.",
|
||||
"commands.computercraft.tp.action": "o tawa pi ilo sona",
|
||||
"commands.computercraft.tp.desc": "o tawa ilo sona. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123).",
|
||||
"commands.computercraft.tp.not_player": "jan ala la, mi ken ala open e sitelen pi ilo sona",
|
||||
"commands.computercraft.tp.not_there": "mi ken ala alasa e ilo sona pi lon ma",
|
||||
"commands.computercraft.tp.synopsis": "mi tawa pi ilo sona e sina.",
|
||||
"commands.computercraft.track.desc": "ilo sona la, mi sitelen e tenpo pali e mute toki. toki wawa /forge track la, mi pana pi nasin sama e sona. mi pona tawa alasa pi tenpo ike.",
|
||||
"commands.computercraft.track.dump.computer": "ilo sona",
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Різні команди для керування комп'ютерами.",
|
||||
"commands.computercraft.tp.action": "Телепортувати до цього комп'ютера",
|
||||
"commands.computercraft.tp.desc": "Телепортувати до розташування комп'ютера. Ви можете вказати або ідентифікатор екземпляра комп'ютера (наприклад, 123) або ідентифікатор комп'ютера (наприклад, #123).",
|
||||
"commands.computercraft.tp.not_player": "Не можна відкрити термінал для не-гравця",
|
||||
"commands.computercraft.tp.not_there": "Не можна визначити у світі розташування комп'ютер",
|
||||
"commands.computercraft.tp.synopsis": "Телепортувати до конкретного комп'ютера.",
|
||||
"commands.computercraft.track.desc": "Відстежує, як довго комп'ютери виконують, а також те, як багато вони обробляють події. Ця інформація представляється аналогічно /forge track і може бути корисною для діагностики лага.",
|
||||
"commands.computercraft.track.dump.computer": "Комп'ютер",
|
||||
|
@@ -45,8 +45,6 @@
|
||||
"commands.computercraft.synopsis": "各种控制计算机的命令.",
|
||||
"commands.computercraft.tp.action": "传送到这台电脑",
|
||||
"commands.computercraft.tp.desc": "传送到计算机的位置. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).",
|
||||
"commands.computercraft.tp.not_player": "无法为非玩家打开终端",
|
||||
"commands.computercraft.tp.not_there": "无法在世界上定位电脑",
|
||||
"commands.computercraft.tp.synopsis": "传送到特定的计算机.",
|
||||
"commands.computercraft.track.desc": "跟踪计算机执行的时间以及它们处理的事件数. 这以/forge track类似的方式呈现信息,可用于诊断滞后.",
|
||||
"commands.computercraft.track.dump.computer": "计算机",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"pack": {
|
||||
"pack_format": 12,
|
||||
"pack_format": 15,
|
||||
"description": "CC: Tweaked"
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
@@ -187,6 +188,11 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
|
||||
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreativeModeTab.Builder newCreativeModeTab() {
|
||||
throw new IllegalStateException("Cannot create creative tab inside tests");
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeIngredients getRecipeIngredients() {
|
||||
throw new UnsupportedOperationException("Cannot query recipes inside tests");
|
||||
|
@@ -353,7 +353,7 @@ public class NetworkTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NetworkPeripheral implements IPeripheral {
|
||||
private static final class NetworkPeripheral implements IPeripheral {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "test";
|
||||
|
@@ -130,7 +130,9 @@ public class Exporter {
|
||||
|
||||
dump.itemNames.put(location.toString(), stack.getHoverName().getString());
|
||||
renderer.captureRender(itemDir.resolve(location.getNamespace()).resolve(location.getPath() + ".png"),
|
||||
() -> Minecraft.getInstance().getItemRenderer().renderAndDecorateFakeItem(transform, stack, 0, 0)
|
||||
() -> {
|
||||
// TODO: Minecraft.getInstance().getItemRenderer().ren(transform, stack, 0, 0)
|
||||
}
|
||||
);
|
||||
}
|
||||
renderer.clearState();
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.export;
|
||||
import com.mojang.blaze3d.pipeline.TextureTarget;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.VertexSorting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -36,7 +37,7 @@ public class ImageRenderer implements AutoCloseable {
|
||||
|
||||
public void setupState() {
|
||||
projectionMatrix = RenderSystem.getProjectionMatrix();
|
||||
RenderSystem.setProjectionMatrix(new Matrix4f().identity().ortho(0, 16, 0, 16, 1000, 3000));
|
||||
RenderSystem.setProjectionMatrix(new Matrix4f().identity().ortho(0, 16, 0, 16, 1000, 3000), VertexSorting.DISTANCE_TO_ORIGIN);
|
||||
|
||||
var transform = RenderSystem.getModelViewStack();
|
||||
transform.pushPose();
|
||||
@@ -48,7 +49,7 @@ public class ImageRenderer implements AutoCloseable {
|
||||
|
||||
public void clearState() {
|
||||
if (projectionMatrix == null) throw new IllegalStateException("Not currently rendering");
|
||||
RenderSystem.setProjectionMatrix(projectionMatrix);
|
||||
RenderSystem.setProjectionMatrix(projectionMatrix, VertexSorting.DISTANCE_TO_ORIGIN);
|
||||
RenderSystem.getModelViewStack().popPose();
|
||||
}
|
||||
|
||||
|
@@ -58,36 +58,36 @@ class CCTestCommand {
|
||||
|
||||
.then(literal("marker").executes(context -> {
|
||||
var player = context.getSource().getPlayerOrException();
|
||||
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.getLevel());
|
||||
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.serverLevel());
|
||||
if (pos == null) return error(context.getSource(), "No nearby test");
|
||||
|
||||
var structureBlock = (StructureBlockEntity) player.getLevel().getBlockEntity(pos);
|
||||
var structureBlock = (StructureBlockEntity) player.level().getBlockEntity(pos);
|
||||
if (structureBlock == null) return error(context.getSource(), "No nearby structure block");
|
||||
var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath());
|
||||
|
||||
// Kill the existing armor stand
|
||||
player
|
||||
.getLevel().getEntities(EntityType.ARMOR_STAND, x -> x.isAlive() && x.getName().getString().equals(info.getTestName()))
|
||||
.serverLevel().getEntities(EntityType.ARMOR_STAND, x -> x.isAlive() && x.getName().getString().equals(info.getTestName()))
|
||||
.forEach(Entity::kill);
|
||||
|
||||
// And create a new one
|
||||
var nbt = new CompoundTag();
|
||||
nbt.putBoolean("Marker", true);
|
||||
nbt.putBoolean("Invisible", true);
|
||||
var armorStand = assertNonNull(EntityType.ARMOR_STAND.create(player.getLevel()));
|
||||
var armorStand = assertNonNull(EntityType.ARMOR_STAND.create(player.level()));
|
||||
armorStand.readAdditionalSaveData(nbt);
|
||||
armorStand.copyPosition(player);
|
||||
armorStand.setCustomName(Component.literal(info.getTestName()));
|
||||
player.getLevel().addFreshEntity(armorStand);
|
||||
player.level().addFreshEntity(armorStand);
|
||||
return 0;
|
||||
}))
|
||||
|
||||
.then(literal("give-computer").executes(context -> {
|
||||
var player = context.getSource().getPlayerOrException();
|
||||
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.getLevel());
|
||||
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.serverLevel());
|
||||
if (pos == null) return error(context.getSource(), "No nearby test");
|
||||
|
||||
var structureBlock = (StructureBlockEntity) player.getLevel().getBlockEntity(pos);
|
||||
var structureBlock = (StructureBlockEntity) player.level().getBlockEntity(pos);
|
||||
if (structureBlock == null) return error(context.getSource(), "No nearby structure block");
|
||||
var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath());
|
||||
|
||||
|
@@ -13,8 +13,8 @@ import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.inventory.TransientCraftingContainer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.CraftingRecipe
|
||||
@@ -31,7 +31,7 @@ class Recipe_Test {
|
||||
@GameTest(template = Structures.DEFAULT)
|
||||
fun Craft_result_has_nbt(context: GameTestHelper) = context.sequence {
|
||||
thenExecute {
|
||||
val container = CraftingContainer(DummyMenu, 3, 3)
|
||||
val container = TransientCraftingContainer(DummyMenu, 3, 3)
|
||||
container.setItem(0, ItemStack(Items.SKELETON_SKULL))
|
||||
container.setItem(1, ItemStack(ModRegistry.Items.COMPUTER_ADVANCED.get()))
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"pack": {
|
||||
"pack_format": 12,
|
||||
"pack_format": 15,
|
||||
"description": "CC: Test"
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
final class DeclaringClassLoader extends ClassLoader {
|
||||
static final DeclaringClassLoader INSTANCE = new DeclaringClassLoader();
|
||||
|
||||
private DeclaringClassLoader() {
|
||||
super(DeclaringClassLoader.class.getClassLoader());
|
||||
}
|
||||
|
||||
Class<?> define(String name, byte[] bytes, ProtectionDomain protectionDomain) throws ClassFormatError {
|
||||
return defineClass(name, bytes, 0, bytes.length, protectionDomain);
|
||||
}
|
||||
}
|
@@ -10,26 +10,44 @@ import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import org.objectweb.asm.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* The underlying generator for {@link LuaFunction}-annotated methods.
|
||||
* <p>
|
||||
* The constructor {@link Generator#Generator(Class, List, Function)} takes in the type of interface to generate (i.e.
|
||||
* {@link LuaMethod}), the context arguments for this function (in the case of {@link LuaMethod}, this will just be
|
||||
* {@link ILuaContext}) and a "wrapper" function to lift a function to execute on the main thread.
|
||||
* <p>
|
||||
* The generated class then implements this interface - the {@code apply} method calls the appropriate methods on
|
||||
* {@link IArguments} to extract the arguments, and then calls the original method.
|
||||
* <p>
|
||||
* As the method is not guaranteed to come from the same classloader, we cannot call the method directly, as that may
|
||||
* result in linkage errors. We instead inject a {@link MethodHandle} into the class as a dynamic constant, and then
|
||||
* call the method with {@link MethodHandle#invokeExact(Object...)}. The method handle is constant, and so this has
|
||||
* equivalent performance to the direct call.
|
||||
*
|
||||
* @param <T> The type of the interface the generated classes implement.
|
||||
*/
|
||||
final class Generator<T> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Generator.class);
|
||||
|
||||
private static final AtomicInteger METHOD_ID = new AtomicInteger();
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private static final String METHOD_NAME = "apply";
|
||||
private static final String[] EXCEPTIONS = new String[]{ Type.getInternalName(LuaException.class) };
|
||||
@@ -42,11 +60,17 @@ final class Generator<T> {
|
||||
|
||||
private static final String INTERNAL_COERCED = Type.getInternalName(Coerced.class);
|
||||
|
||||
private static final ConstantDynamic METHOD_CONSTANT = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), new Handle(
|
||||
H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
|
||||
MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).descriptorString(), false
|
||||
));
|
||||
|
||||
private final Class<T> base;
|
||||
private final List<Class<?>> context;
|
||||
|
||||
private final String[] interfaces;
|
||||
private final String methodDesc;
|
||||
private final String classPrefix;
|
||||
|
||||
private final Function<T, T> wrap;
|
||||
|
||||
@@ -64,6 +88,8 @@ final class Generator<T> {
|
||||
for (var klass : context) methodDesc.append(Type.getDescriptor(klass));
|
||||
methodDesc.append(DESC_ARGUMENTS).append(")").append(DESC_METHOD_RESULT);
|
||||
this.methodDesc = methodDesc.toString();
|
||||
|
||||
classPrefix = Generator.class.getPackageName() + "." + base.getSimpleName() + "$";
|
||||
}
|
||||
|
||||
Optional<T> getMethod(Method method) {
|
||||
@@ -110,11 +136,17 @@ final class Generator<T> {
|
||||
var target = Modifier.isStatic(modifiers) ? method.getParameterTypes()[0] : method.getDeclaringClass();
|
||||
|
||||
try {
|
||||
var className = method.getDeclaringClass().getName() + "$cc$" + method.getName() + METHOD_ID.getAndIncrement();
|
||||
var bytes = generate(className, target, method, annotation.unsafe());
|
||||
var handle = LOOKUP.unreflect(method);
|
||||
|
||||
// Convert the handle from one of the form (target, ...) -> ret type to (Object, ...) -> Object. This both
|
||||
// handles the boxing of primitives for us, and ensures our bytecode does not reference any external types.
|
||||
// We could handle the conversion to MethodResult here too, but it doesn't feel worth it.
|
||||
var widenedHandle = handle.asType(widenMethodType(handle.type(), target));
|
||||
|
||||
var bytes = generate(classPrefix + method.getName(), target, method, widenedHandle.type().descriptorString(), annotation.unsafe());
|
||||
if (bytes == null) return Optional.empty();
|
||||
|
||||
var klass = DeclaringClassLoader.INSTANCE.define(className, bytes, method.getDeclaringClass().getProtectionDomain());
|
||||
var klass = LOOKUP.defineHiddenClassWithClassData(bytes, widenedHandle, true).lookupClass();
|
||||
|
||||
var instance = klass.asSubclass(base).getDeclaredConstructor().newInstance();
|
||||
return Optional.of(annotation.mainThread() ? wrap.apply(instance) : instance);
|
||||
@@ -122,16 +154,29 @@ final class Generator<T> {
|
||||
LOG.error("Error generating wrapper for {}.", name, e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodType widenMethodType(MethodType source, Class<?> target) {
|
||||
// Treat the target argument as just Object - we'll do the cast in the method handle.
|
||||
var args = source.parameterArray();
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i] == target) args[i] = Object.class;
|
||||
}
|
||||
|
||||
// And convert the return value to Object if needed.
|
||||
var ret = source.returnType();
|
||||
return ret == void.class || ret == MethodResult.class || ret == Object[].class
|
||||
? MethodType.methodType(ret, args)
|
||||
: MethodType.methodType(Object.class, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] generate(String className, Class<?> target, Method method, boolean unsafe) {
|
||||
private byte[] generate(String className, Class<?> target, Method targetMethod, String targetDescriptor, boolean unsafe) {
|
||||
var internalName = className.replace(".", "/");
|
||||
|
||||
// Construct a public final class which extends Object and implements MethodInstance.Delegate
|
||||
var cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, internalName, null, "java/lang/Object", interfaces);
|
||||
cw.visit(V17, ACC_PUBLIC | ACC_FINAL, internalName, null, "java/lang/Object", interfaces);
|
||||
cw.visitSource("CC generated method", null);
|
||||
|
||||
{ // Constructor just invokes super.
|
||||
@@ -148,35 +193,26 @@ final class Generator<T> {
|
||||
var mw = cw.visitMethod(ACC_PUBLIC, METHOD_NAME, methodDesc, null, EXCEPTIONS);
|
||||
mw.visitCode();
|
||||
|
||||
// If we're an instance method, load the this parameter.
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
mw.visitVarInsn(ALOAD, 1);
|
||||
mw.visitTypeInsn(CHECKCAST, Type.getInternalName(target));
|
||||
}
|
||||
mw.visitLdcInsn(METHOD_CONSTANT);
|
||||
|
||||
// If we're an instance method, load the target as the first argument.
|
||||
if (!Modifier.isStatic(targetMethod.getModifiers())) mw.visitVarInsn(ALOAD, 1);
|
||||
|
||||
var argIndex = 0;
|
||||
for (var genericArg : method.getGenericParameterTypes()) {
|
||||
var loadedArg = loadArg(mw, target, method, unsafe, genericArg, argIndex);
|
||||
for (var genericArg : targetMethod.getGenericParameterTypes()) {
|
||||
var loadedArg = loadArg(mw, target, targetMethod, unsafe, genericArg, argIndex);
|
||||
if (loadedArg == null) return null;
|
||||
if (loadedArg) argIndex++;
|
||||
}
|
||||
|
||||
mw.visitMethodInsn(
|
||||
Modifier.isStatic(method.getModifiers()) ? INVOKESTATIC : INVOKEVIRTUAL,
|
||||
Type.getInternalName(method.getDeclaringClass()), method.getName(),
|
||||
Type.getMethodDescriptor(method), false
|
||||
);
|
||||
mw.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact", targetDescriptor, false);
|
||||
|
||||
// We allow a reasonable amount of flexibility on the return value's type. Alongside the obvious MethodResult,
|
||||
// we convert basic types into an immediate result.
|
||||
var ret = method.getReturnType();
|
||||
var ret = targetMethod.getReturnType();
|
||||
if (ret != MethodResult.class) {
|
||||
if (ret == void.class) {
|
||||
mw.visitMethodInsn(INVOKESTATIC, INTERNAL_METHOD_RESULT, "of", "()" + DESC_METHOD_RESULT, false);
|
||||
} else if (ret.isPrimitive()) {
|
||||
var boxed = Primitives.wrap(ret);
|
||||
mw.visitMethodInsn(INVOKESTATIC, Type.getInternalName(boxed), "valueOf", "(" + Type.getDescriptor(ret) + ")" + Type.getDescriptor(boxed), false);
|
||||
mw.visitMethodInsn(INVOKESTATIC, INTERNAL_METHOD_RESULT, "of", "(Ljava/lang/Object;)" + DESC_METHOD_RESULT, false);
|
||||
} else if (ret == Object[].class) {
|
||||
mw.visitMethodInsn(INVOKESTATIC, INTERNAL_METHOD_RESULT, "of", "([Ljava/lang/Object;)" + DESC_METHOD_RESULT, false);
|
||||
} else {
|
||||
@@ -199,7 +235,6 @@ final class Generator<T> {
|
||||
private Boolean loadArg(MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex) {
|
||||
if (genericArg == target) {
|
||||
mw.visitVarInsn(ALOAD, 1);
|
||||
mw.visitTypeInsn(CHECKCAST, Type.getInternalName(target));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,15 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
@SuppressWarnings("GuardedBy") // FIXME: Hard to know what the correct thing to do is.
|
||||
public final class ComputerThread {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ComputerThread.class);
|
||||
private static final ThreadFactory monitorFactory = ThreadUtils.factory("Computer-Monitor");
|
||||
|
||||
/**
|
||||
* A factory for the monitor thread. We want this a slightly higher priority than normal to ensure that the computer
|
||||
* thread is interrupted. This spends most of it its time idle, so should be safe.
|
||||
*/
|
||||
private static final ThreadFactory monitorFactory = ThreadUtils.builder("Computer-Monitor")
|
||||
.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2)
|
||||
.build();
|
||||
|
||||
private static final ThreadFactory workerFactory = ThreadUtils.lowPriorityFactory("Computer-Worker");
|
||||
|
||||
/**
|
||||
|
@@ -13,13 +13,13 @@ import java.util.concurrent.TimeUnit;
|
||||
* <p>
|
||||
* This is useful for emulators, where we'll never make any main thread calls.
|
||||
*/
|
||||
public class NoWorkMainThreadScheduler implements MainThreadScheduler {
|
||||
public final class NoWorkMainThreadScheduler implements MainThreadScheduler {
|
||||
@Override
|
||||
public Executor createExecutor(MetricsObserver observer) {
|
||||
return new ExecutorImpl();
|
||||
}
|
||||
|
||||
private static class ExecutorImpl implements Executor {
|
||||
private static final class ExecutorImpl implements Executor {
|
||||
@Override
|
||||
public boolean enqueue(Runnable task) {
|
||||
throw new IllegalStateException("Cannot schedule tasks");
|
||||
|
@@ -1,3 +1,9 @@
|
||||
# New features in CC: Tweaked 1.108.1
|
||||
|
||||
Several bug fixes:
|
||||
* Prevent no-opped players breaking or placing command computers.
|
||||
* Allow using `@LuaFunction`-annotated methods on classes defined in child classloaders.
|
||||
|
||||
# New features in CC: Tweaked 1.108.0
|
||||
|
||||
* Remove compression from terminal/monitor packets. Vanilla applies its own compression, so this ends up being less helpful than expected.
|
||||
|
@@ -1,15 +1,7 @@
|
||||
New features in CC: Tweaked 1.108.0
|
||||
|
||||
* Remove compression from terminal/monitor packets. Vanilla applies its own compression, so this ends up being less helpful than expected.
|
||||
* `/computercraft` command now supports permission mods.
|
||||
* Split some GUI textures into sprite sheets.
|
||||
* Support the `%g` character class in string pattern matching.
|
||||
New features in CC: Tweaked 1.108.1
|
||||
|
||||
Several bug fixes:
|
||||
* Fix crash when playing some modded records via a disk drive.
|
||||
* Fix race condition when computers attach or detach from a monitor.
|
||||
* Fix the "max websocket message" config option not being read.
|
||||
* `tostring` now correctly obeys `__name`.
|
||||
* Fix several inconsistencies with pattern matching character classes.
|
||||
* Prevent no-opped players breaking or placing command computers.
|
||||
* Allow using `@LuaFunction`-annotated methods on classes defined in child classloaders.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
@@ -11,12 +11,14 @@ import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.NamedMethod;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.*;
|
||||
|
||||
import static dan200.computercraft.test.core.ContramapMatcher.contramap;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -116,6 +118,33 @@ public class GeneratorTest {
|
||||
assertThat(methods, contains(named("withUnsafe")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassNotAccessible() throws IOException, ReflectiveOperationException, LuaException {
|
||||
var basicName = Basic.class.getName().replace('.', '/');
|
||||
|
||||
// Load our Basic class, rewriting it to be a separate (hidden) class which is not part of the same nest as
|
||||
// the existing Basic.
|
||||
ClassReader reader;
|
||||
try (var input = getClass().getClassLoader().getResourceAsStream(basicName + ".class")) {
|
||||
reader = new ClassReader(Objects.requireNonNull(input, "Cannot find " + basicName));
|
||||
}
|
||||
var writer = new ClassWriter(reader, 0);
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM9, writer) {
|
||||
@Override
|
||||
public void visitNestHost(String nestHost) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
}
|
||||
}, 0);
|
||||
|
||||
var klass = MethodHandles.lookup().defineHiddenClass(writer.toByteArray(), true).lookupClass();
|
||||
|
||||
var methods = GENERATOR.getMethods(klass);
|
||||
assertThat(apply(methods, klass.getConstructor().newInstance(), "go"), equalTo(MethodResult.of()));
|
||||
}
|
||||
|
||||
public static class Basic {
|
||||
@LuaFunction
|
||||
public final void go() {
|
||||
|
@@ -53,6 +53,7 @@ dependencies {
|
||||
exclude("net.fabricmc", "fabric-loader")
|
||||
exclude("net.fabricmc.fabric-api")
|
||||
}
|
||||
|
||||
modClientRuntimeOnly(libs.bundles.externalMods.fabric.runtime) {
|
||||
exclude("net.fabricmc", "fabric-loader")
|
||||
exclude("net.fabricmc.fabric-api")
|
||||
|
@@ -47,8 +47,6 @@
|
||||
"commands.computercraft.synopsis": "Various commands for controlling computers.",
|
||||
"commands.computercraft.tp.action": "Teleport to this computer",
|
||||
"commands.computercraft.tp.desc": "Teleport to the location of a computer. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123).",
|
||||
"commands.computercraft.tp.not_player": "Cannot open terminal for non-player",
|
||||
"commands.computercraft.tp.not_there": "Cannot locate computer in the world",
|
||||
"commands.computercraft.tp.synopsis": "Teleport to a specific computer.",
|
||||
"commands.computercraft.track.desc": "Track how long computers execute for, as well as how many events they handle. This presents information in a similar way to /forge track and can be useful for diagnosing lag.",
|
||||
"commands.computercraft.track.dump.computer": "Computer",
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_monitor", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:skull_cloudy"]}
|
||||
"rewards": {"recipes": ["computercraft:skull_cloudy"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_computer", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:skull_dan200"]}
|
||||
"rewards": {"recipes": ["computercraft:skull_dan200"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -12,5 +12,6 @@
|
||||
"has_the_recipe": {"conditions": {"recipe": "computercraft:cable"}, "trigger": "minecraft:recipe_unlocked"}
|
||||
},
|
||||
"requirements": [["has_computer", "has_modem", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:cable"]}
|
||||
"rewards": {"recipes": ["computercraft:cable"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_components", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:computer_advanced"]}
|
||||
"rewards": {"recipes": ["computercraft:computer_advanced"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_components", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:computer_advanced_upgrade"]}
|
||||
"rewards": {"recipes": ["computercraft:computer_advanced_upgrade"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_components", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:computer_command"]}
|
||||
"rewards": {"recipes": ["computercraft:computer_command"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_redstone", "has_the_recipe"]],
|
||||
"rewards": {"recipes": ["computercraft:computer_normal"]}
|
||||
"rewards": {"recipes": ["computercraft:computer_normal"]},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user