diff --git a/gradle.properties b/gradle.properties
index 3ad02043f..b0d7e85fb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,4 +15,4 @@ isUnstable=true
modVersion=1.115.1
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
-mcVersion=1.21.4
+mcVersion=1.21.5
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d782bfa38..72a768c5d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,19 +7,19 @@
# Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle.
# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
-fabric-api = "0.118.0+1.21.4"
+fabric-api = "0.120.0+1.21.5"
fabric-loader = "0.16.10"
-neoForge = "21.4.101-beta"
+neoForge = "21.5.49-beta"
neoMergeTool = "2.0.0"
mixin = "0.8.5"
-parchment = "2024.12.07"
-parchmentMc = "1.21.4"
-yarn = "1.21.4+build.1"
+parchment = "2025.04.19"
+parchmentMc = "1.21.5"
+yarn = "1.21.5+build.1"
# Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.15"
guava = "33.3.1-jre"
-netty = "4.1.115.Final"
+netty = "4.1.118.Final"
slf4j = "2.0.16"
# Core dependencies (independent of Minecraft)
@@ -38,14 +38,14 @@ nightConfig = "3.8.1"
# Minecraft mods
emi = "1.1.7+1.21"
fabricPermissions = "0.3.3"
-iris-fabric = "1.8.8+1.21.4-fabric"
-iris-forge = "1.8.8+1.21.4-neoforge"
+iris-fabric = "1.8.11+1.21.5-fabric"
+iris-forge = "1.8.11+1.21.5-neoforge"
jei = "19.8.2.99"
modmenu = "13.0.2"
moreRed = "6.0.0.3"
rei = "18.0.800"
-sodium-fabric = "mc1.21.4-0.6.10-fabric"
-sodium-forge = "mc1.21.4-0.6.10-neoforge"
+sodium-fabric = "mc1.21.5-0.6.12-fabric"
+sodium-forge = "mc1.21.5-0.6.12-neoforge"
mixinExtra = "0.3.5"
create-forge = "6.0.0-6"
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
@@ -69,7 +69,7 @@ ideaExt = "1.1.7"
illuaminate = "0.1.0-83-g1131f68"
lwjgl = "3.3.3"
minotaur = "2.8.7"
-modDevGradle = "2.0.78"
+modDevGradle = "2.0.82"
nullAway = "0.12.4"
shadow = "8.3.1"
spotless = "7.0.2"
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/StandaloneModel.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/StandaloneModel.java
new file mode 100644
index 000000000..008ed08ce
--- /dev/null
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/StandaloneModel.java
@@ -0,0 +1,153 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.api.client;
+
+import com.google.common.base.Suppliers;
+import com.mojang.blaze3d.vertex.PoseStack;
+import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.turtle.TurtleSide;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
+import net.minecraft.client.renderer.Sheets;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.block.model.ItemTransform;
+import net.minecraft.client.renderer.item.BlockModelWrapper;
+import net.minecraft.client.renderer.item.ItemModel;
+import net.minecraft.client.renderer.item.ItemModelResolver;
+import net.minecraft.client.renderer.item.ItemStackRenderState;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.resources.model.BlockModelRotation;
+import net.minecraft.client.resources.model.ModelBaker;
+import net.minecraft.client.resources.model.ResolvedModel;
+import net.minecraft.core.component.DataComponentPatch;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.ARGB;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.item.ItemDisplayContext;
+import net.minecraft.world.item.ItemStack;
+import org.joml.Vector3f;
+import org.jspecify.annotations.Nullable;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * A standalone model.
+ *
+ * This is very similar to vanilla's {@link BlockModelWrapper}, but suitable for use in both {@link ItemModel}s and
+ * block models. This is primarily intended for use with {@link TurtleUpgradeModel}s.
+ */
+public final class StandaloneModel {
+ private final List quads;
+ private final boolean useBlockLight;
+ private final TextureAtlasSprite particleIcon;
+ private final RenderType renderType;
+ private final Supplier extents;
+
+ /**
+ * Construct a new {@link StandaloneModel}.
+ *
+ * @param quads The list of quads which form this model.
+ * @param usesBlockLight Whether this uses block lighting. See {@link ItemStackRenderState.LayerRenderState#setUsesBlockLight(boolean)}.
+ * @param particleIcon The sprite for the model's particles. See {@link ItemStackRenderState.LayerRenderState#setParticleIcon(TextureAtlasSprite)}.
+ * @param renderType The render type for this model.
+ */
+ public StandaloneModel(List quads, boolean usesBlockLight, TextureAtlasSprite particleIcon, RenderType renderType) {
+ this.quads = quads;
+ this.useBlockLight = usesBlockLight;
+ this.particleIcon = particleIcon;
+ this.renderType = renderType;
+ this.extents = Suppliers.memoize(() -> BlockModelWrapper.computeExtents(quads));
+ }
+
+ /**
+ * Load a model from a {@link ModelBaker} and bake it.
+ *
+ * @param model The model id to load.
+ * @param baker The model baker.
+ * @return The baked {@link StandaloneModel}.
+ */
+ public static StandaloneModel of(ResourceLocation model, ModelBaker baker) {
+ return of(baker.getModel(model), baker);
+ }
+
+ /**
+ * Bake a {@link ResolvedModel} into a {@link StandaloneModel}.
+ *
+ * @param model The resolved model.
+ * @param baker The model baker.
+ * @return The baked {@link StandaloneModel}.
+ */
+ public static StandaloneModel of(ResolvedModel model, ModelBaker baker) {
+ var slots = model.getTopTextureSlots();
+ return new StandaloneModel(
+ model.bakeTopGeometry(slots, baker, BlockModelRotation.X0_Y0).getAll(),
+ model.getTopGuiLight().lightLikeBlock(),
+ model.resolveParticleSprite(slots, baker),
+ Sheets.translucentItemSheet()
+ );
+ }
+
+ /**
+ * Set up an {@link ItemStackRenderState.LayerRenderState} to render this model.
+ *
+ * @param layer The layer to set up.
+ * @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
+ * @see TurtleUpgradeModel#renderForItem(ITurtleUpgrade, TurtleSide, DataComponentPatch, ItemStackRenderState, ItemModelResolver, ItemTransform, int)
+ */
+ public void setupItemLayer(ItemStackRenderState.LayerRenderState layer) {
+ layer.setExtents(extents);
+ layer.setRenderType(renderType);
+ layer.setUsesBlockLight(useBlockLight);
+ layer.setParticleIcon(particleIcon);
+ layer.prepareQuadList().addAll(quads);
+ }
+
+ /**
+ * Render the model directly.
+ *
+ * @param transform The current pose stack transformations.
+ * @param buffers The buffer source to use for rendering.
+ * @param light The current light texture coordinate.
+ * @param overlay The current overlay texture coordinate.
+ * @see TurtleUpgradeModel#renderForLevel(ITurtleUpgrade, ITurtleAccess, TurtleSide, DataComponentPatch, PoseStack, MultiBufferSource, int, int)
+ */
+ public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
+ render(transform, buffers, light, overlay, null);
+ }
+
+ /**
+ * Render the model directly.
+ *
+ * @param transform The current pose stack transformations.
+ * @param buffers The buffer source to use for rendering.
+ * @param light The current light texture coordinate.
+ * @param overlay The current overlay texture coordinate.
+ * @param tints The tints for this model.
+ * @see TurtleUpgradeModel#renderForLevel(ITurtleUpgrade, ITurtleAccess, TurtleSide, DataComponentPatch, PoseStack, MultiBufferSource, int, int)
+ */
+ public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay, int @Nullable [] tints) {
+ var pose = transform.last();
+ var buffer = buffers.getBuffer(renderType);
+ for (var quad : quads) {
+ float r, g, b, a;
+ var idx = quad.tintIndex();
+ if (tints != null && idx >= 0 && idx < tints.length) {
+ var tint = tints[idx];
+ r = ARGB.red(tint) / 255.0f;
+ g = ARGB.green(tint) / 255.0f;
+ b = ARGB.blue(tint) / 255.0f;
+ a = ARGB.alpha(tint) / 255.0f;
+ } else {
+ r = g = b = a = 1.0f;
+ }
+
+ buffer.putBulkData(pose, quad, r, g, b, a, light, overlay);
+ }
+ }
+}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/TransformedModel.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/TransformedModel.java
deleted file mode 100644
index 7e94488f5..000000000
--- a/projects/common-api/src/client/java/dan200/computercraft/api/client/TransformedModel.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-
-package dan200.computercraft.api.client;
-
-import com.mojang.math.Transformation;
-import dan200.computercraft.impl.client.ClientPlatformHelper;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.resources.model.BakedModel;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.item.ItemStack;
-
-/**
- * A model to render, combined with a transformation matrix to apply.
- */
-public sealed interface TransformedModel permits TransformedModel.Baked, TransformedModel.Item {
- record Baked(BakedModel model) implements TransformedModel {
- }
-
- record Item(ItemStack stack, Transformation transformation) implements TransformedModel {
- }
-
- static TransformedModel of(BakedModel model) {
- return new TransformedModel.Baked(model);
- }
-
- /**
- * Look up a model in the model bakery and construct a {@link TransformedModel} with no transformation.
- *
- * @param location The location of the model to load.
- * @return The new {@link TransformedModel} instance.
- */
- static TransformedModel of(ResourceLocation location) {
- var modelManager = Minecraft.getInstance().getModelManager();
- return of(ClientPlatformHelper.get().getModel(modelManager, location));
- }
-
- static TransformedModel of(ItemStack item, Transformation transform) {
- return new TransformedModel.Item(item, transform);
- }
-}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/ItemUpgradeModel.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/ItemUpgradeModel.java
new file mode 100644
index 000000000..190bf5361
--- /dev/null
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/ItemUpgradeModel.java
@@ -0,0 +1,102 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.api.client.turtle;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.math.Axis;
+import com.mojang.math.Transformation;
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.turtle.TurtleSide;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.block.model.ItemTransform;
+import net.minecraft.client.renderer.item.ItemModelResolver;
+import net.minecraft.client.renderer.item.ItemStackRenderState;
+import net.minecraft.client.renderer.special.SpecialModelRenderer;
+import net.minecraft.client.resources.model.ModelBaker;
+import net.minecraft.core.component.DataComponentPatch;
+import net.minecraft.util.Mth;
+import net.minecraft.world.item.ItemDisplayContext;
+import net.minecraft.world.item.ItemStack;
+import org.joml.Matrix4f;
+import org.jspecify.annotations.Nullable;
+
+final class ItemUpgradeModel implements TurtleUpgradeModel {
+ static final TurtleUpgradeModel.Unbaked UNBAKED = new Unbaked();
+ static final TurtleUpgradeModel INSTANCE = new ItemUpgradeModel<>();
+
+ private static final TransformedRenderer LEFT = computeRenderer(TurtleSide.LEFT);
+ private static final TransformedRenderer RIGHT = computeRenderer(TurtleSide.RIGHT);
+
+ private ItemUpgradeModel() {
+ }
+
+ @Override
+ public void renderForItem(T upgrade, TurtleSide side, DataComponentPatch data, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed) {
+ var childState = new ItemStackRenderState();
+ resolver.updateForTopItem(childState, upgrade.getUpgradeItem(data), ItemDisplayContext.NONE, null, null, seed);
+ if (!childState.isEmpty()) {
+ var layer = renderer.newLayer();
+ layer.setTransform(transform);
+ layer.setupSpecialModel(getRenderer(side), childState);
+ }
+ }
+
+ @Override
+ public void renderForLevel(T upgrade, ITurtleAccess turtle, TurtleSide side, DataComponentPatch data, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
+ transform.mulPose(getRenderer(side).transform().getMatrix());
+ transform.mulPose(Axis.YP.rotation(Mth.PI));
+ Minecraft.getInstance().getItemRenderer().renderStatic(
+ upgrade.getUpgradeItem(data), ItemDisplayContext.FIXED, light, overlay, transform, buffers, turtle.getLevel(), 0
+ );
+ }
+
+ private static final class Unbaked implements TurtleUpgradeModel.Unbaked {
+ @Override
+ public TurtleUpgradeModel bake(ModelBaker baker) {
+ return INSTANCE;
+ }
+
+ @Override
+ public void resolveDependencies(Resolver resolver) {
+ }
+ }
+
+ private static TransformedRenderer computeRenderer(TurtleSide side) {
+ var pose = new Matrix4f();
+ pose.translate(0.5f, 0.5f, 0.5f);
+ pose.rotate(Axis.YN.rotationDegrees(90f));
+ pose.rotate(Axis.ZP.rotationDegrees(90f));
+ pose.translate(0.0f, 0.0f, side == TurtleSide.RIGHT ? -0.4065f : 0.4065f);
+ return new TransformedRenderer(new Transformation(pose));
+ }
+
+ private static TransformedRenderer getRenderer(TurtleSide side) {
+ return switch (side) {
+ case LEFT -> LEFT;
+ case RIGHT -> RIGHT;
+ };
+ }
+
+ private record TransformedRenderer(Transformation transform) implements SpecialModelRenderer {
+ @Override
+ public void render(
+ @Nullable ItemStackRenderState state, ItemDisplayContext itemDisplayContext, PoseStack poseStack,
+ MultiBufferSource multiBufferSource, int overlay, int light, boolean bl
+ ) {
+ if (state == null) return;
+ poseStack.pushPose();
+ poseStack.mulPose(transform.getMatrix());
+ state.render(poseStack, multiBufferSource, overlay, light);
+ poseStack.popPose();
+ }
+
+ @Override
+ public @Nullable ItemStackRenderState extractArgument(ItemStack itemStack) {
+ return null;
+ }
+ }
+}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModel.java
similarity index 64%
rename from projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java
rename to projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModel.java
index ede3e7675..3100d2b5a 100644
--- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModel.java
@@ -8,19 +8,19 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.upgrades.UpgradeType;
/**
- * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
+ * A functional interface to register a {@link TurtleUpgradeModel} for a class of turtle upgrades.
*
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
* multiple loaders.
*/
@FunctionalInterface
-public interface RegisterTurtleUpgradeModeller {
+public interface RegisterTurtleUpgradeModel {
/**
- * Register a {@link TurtleUpgradeModeller}.
+ * Register a {@link TurtleUpgradeModel}.
*
- * @param type The turtle upgrade type.
- * @param modeller The upgrade modeller.
- * @param The type of the turtle upgrade.
+ * @param type The turtle upgrade type.
+ * @param mode The unbaked upgrade model.
+ * @param The type of the turtle upgrade.
*/
- void register(UpgradeType type, TurtleUpgradeModeller modeller);
+ void register(UpgradeType type, TurtleUpgradeModel.Unbaked super T> mode);
}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/SidedUpgradeModel.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/SidedUpgradeModel.java
new file mode 100644
index 000000000..4020607d2
--- /dev/null
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/SidedUpgradeModel.java
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.api.client.turtle;
+
+import dan200.computercraft.api.client.StandaloneModel;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.turtle.TurtleSide;
+import net.minecraft.client.resources.model.ModelBaker;
+import net.minecraft.core.component.DataComponentPatch;
+import net.minecraft.resources.ResourceLocation;
+
+record SidedUpgradeModel(
+ StandaloneModel left, StandaloneModel right
+) implements TurtleUpgradeModelViaStandalone {
+ @Override
+ public StandaloneModel getModel(T upgrade, TurtleSide side, DataComponentPatch data) {
+ return switch (side) {
+ case LEFT -> left();
+ case RIGHT -> right();
+ };
+ }
+
+ record Unbaked(
+ ResourceLocation left, ResourceLocation right
+ ) implements TurtleUpgradeModel.Unbaked {
+
+ @Override
+ public TurtleUpgradeModel bake(ModelBaker baker) {
+ return new SidedUpgradeModel<>(StandaloneModel.of(left(), baker), StandaloneModel.of(right(), baker));
+ }
+
+ @Override
+ public void resolveDependencies(Resolver resolver) {
+ resolver.markDependency(left());
+ resolver.markDependency(right());
+ }
+ }
+}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModel.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModel.java
new file mode 100644
index 000000000..786c288b4
--- /dev/null
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModel.java
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.api.client.turtle;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.turtle.TurtleSide;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.block.model.ItemTransform;
+import net.minecraft.client.renderer.item.ItemModel;
+import net.minecraft.client.renderer.item.ItemModelResolver;
+import net.minecraft.client.renderer.item.ItemStackRenderState;
+import net.minecraft.client.resources.model.ModelBaker;
+import net.minecraft.client.resources.model.ResolvableModel;
+import net.minecraft.core.component.DataComponentPatch;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.item.ItemDisplayContext;
+import net.minecraft.world.item.ItemStack;
+
+/**
+ * The model for a {@link ITurtleUpgrade}.
+ *
+ * Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
+ * modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
+ * on Forge.
+ *
+ *
Example
+ * Fabric
+ * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_model}
+ *
+ * Forge
+ * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_model}
+ *
+ * @param The type of turtle upgrade this modeller applies to.
+ * @see RegisterTurtleUpgradeModel For multi-loader registration support.
+ */
+public interface TurtleUpgradeModel {
+ /**
+ * Render this upgrade to an {@link ItemStackRenderState}. This is used for rendering the item form of the upgrade.
+ *
+ * @param upgrade The upgrade being rendered.
+ * @param side Which side of the turtle (left or right) the upgrade resides on.
+ * @param data Upgrade data instance for current turtle side.
+ * @param renderer The render state to draw to.
+ * @param resolver The model resolver.
+ * @param transform The root model's transformation.
+ * @param seed The current model seed.
+ * @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
+ */
+ void renderForItem(T upgrade, TurtleSide side, DataComponentPatch data, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed);
+
+ /**
+ * Render this upgrade to a {@link MultiBufferSource}. This is used for rendering the block-entity form of the
+ * upgrade.
+ *
+ * @param upgrade The upgrade being rendered.
+ * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models.
+ * @param side Which side of the turtle (left or right) the upgrade resides on.
+ * @param data Upgrade data instance for current turtle side.
+ * @param transform The current pose stack.
+ * @param buffers The buffers to render to.
+ * @param light The lightmap coordinate.
+ * @param overlay The overlay coordinate.
+ */
+ void renderForLevel(T upgrade, ITurtleAccess turtle, TurtleSide side, DataComponentPatch data, PoseStack transform, MultiBufferSource buffers, int light, int overlay);
+
+ /**
+ * An unbaked turtle model. Much like other unbaked models (e.g. {@link ItemModel.Unbaked}), this should resolve
+ * any dependencies and returned the fully-resolved model.
+ *
+ * @param The type of turtle upgrade for this model.
+ */
+ interface Unbaked extends ResolvableModel {
+ TurtleUpgradeModel bake(ModelBaker baker);
+ }
+
+ /**
+ * A basic {@link TurtleUpgradeModel} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(DataComponentPatch)
+ * upgrade item}.
+ *
+ * This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
+ * model type. It will not appear correct for 3D models with additional depth, such as blocks.
+ *
+ * @param The type of the turtle upgrade.
+ * @return The constructed modeller.
+ */
+ static TurtleUpgradeModel.Unbaked super T> flatItem() {
+ return ItemUpgradeModel.UNBAKED;
+ }
+
+ /**
+ * Construct a {@link TurtleUpgradeModel} which has a single model for the left and right side.
+ *
+ * @param left The model to use on the left.
+ * @param right The model to use on the right.
+ * @param The type of the turtle upgrade.
+ * @return The constructed modeller.
+ */
+ static TurtleUpgradeModel.Unbaked sided(ResourceLocation left, ResourceLocation right) {
+ return new SidedUpgradeModel.Unbaked<>(left, right);
+ }
+}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModelViaStandalone.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModelViaStandalone.java
new file mode 100644
index 000000000..81ea70ad6
--- /dev/null
+++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModelViaStandalone.java
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.api.client.turtle;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import dan200.computercraft.api.client.StandaloneModel;
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.turtle.TurtleSide;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.block.model.ItemTransform;
+import net.minecraft.client.renderer.item.ItemModelResolver;
+import net.minecraft.client.renderer.item.ItemStackRenderState;
+import net.minecraft.core.component.DataComponentPatch;
+
+public interface TurtleUpgradeModelViaStandalone extends TurtleUpgradeModel {
+ StandaloneModel getModel(T upgrade, TurtleSide side, DataComponentPatch data);
+
+ @Override
+ default void renderForItem(T upgrade, TurtleSide side, DataComponentPatch data, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed) {
+ var layer = renderer.newLayer();
+ layer.setTransform(transform);
+ getModel(upgrade, side, data).setupItemLayer(layer);
+ }
+
+ @Override
+ default void renderForLevel(T upgrade, ITurtleAccess turtle, TurtleSide side, DataComponentPatch data, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
+ getModel(upgrade, side, data).render(transform, buffers, light, overlay);
+ }
+}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java
deleted file mode 100644
index 347d0314f..000000000
--- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-package dan200.computercraft.api.client.turtle;
-
-import dan200.computercraft.api.client.TransformedModel;
-import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.api.turtle.ITurtleUpgrade;
-import dan200.computercraft.api.turtle.TurtleSide;
-import net.minecraft.client.resources.model.UnbakedModel;
-import net.minecraft.core.component.DataComponentPatch;
-import net.minecraft.resources.ResourceLocation;
-import org.jspecify.annotations.Nullable;
-
-import java.util.stream.Stream;
-
-/**
- * Provides models for a {@link ITurtleUpgrade}.
- *
- * Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
- * modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
- * on Forge.
- *
- *
Example
- * Fabric
- * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
- *
- * Forge
- * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
- *
- * @param The type of turtle upgrade this modeller applies to.
- * @see RegisterTurtleUpgradeModeller For multi-loader registration support.
- */
-public interface TurtleUpgradeModeller {
- /**
- * Obtain the model to be used when rendering a turtle peripheral.
- *
- * When the current turtle is {@literal null}, this function should be constant for a given upgrade, side and data.
- *
- * @param upgrade The upgrade that you're getting the model for.
- * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models.
- * @param side Which side of the turtle (left or right) the upgrade resides on.
- * @param data Upgrade data instance for current turtle side.
- * @return The model that you wish to be used to render your upgrade.
- */
- TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data);
-
- /**
- * Get the models that this turtle modeller depends on.
- *
- * Models included in this stream will be loaded and baked alongside item and block models, and so may be referenced
- * by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
- * by other means.
- *
- * @return A list of models that this modeller depends on.
- * @see UnbakedModel#resolveDependencies(UnbakedModel.Resolver)
- */
- default Stream getDependencies() {
- return Stream.of();
- }
-
- /**
- * A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(DataComponentPatch)}
- * upgrade item}.
- *
- * This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
- * model type. It will not appear correct for 3D models with additional depth, such as blocks.
- *
- * @param The type of the turtle upgrade.
- * @return The constructed modeller.
- */
- @SuppressWarnings("unchecked")
- static TurtleUpgradeModeller flatItem() {
- return (TurtleUpgradeModeller) TurtleUpgradeModellers.UPGRADE_ITEM;
- }
-
- /**
- * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
- *
- * @param left The model to use on the left.
- * @param right The model to use on the right.
- * @param The type of the turtle upgrade.
- * @return The constructed modeller.
- */
- static TurtleUpgradeModeller sided(ResourceLocation left, ResourceLocation right) {
- return new TurtleUpgradeModeller<>() {
- @Override
- public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
- return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
- }
-
- @Override
- public Stream getDependencies() {
- return Stream.of(left, right);
- }
- };
- }
-}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java
deleted file mode 100644
index 8612ffb23..000000000
--- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-package dan200.computercraft.api.client.turtle;
-
-import com.mojang.math.Axis;
-import com.mojang.math.Transformation;
-import dan200.computercraft.api.client.TransformedModel;
-import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.api.turtle.ITurtleUpgrade;
-import dan200.computercraft.api.turtle.TurtleSide;
-import net.minecraft.core.component.DataComponentPatch;
-import org.joml.Matrix4f;
-import org.jspecify.annotations.Nullable;
-
-final class TurtleUpgradeModellers {
- private static final Transformation leftTransform = getMatrixFor(TurtleSide.LEFT);
- private static final Transformation rightTransform = getMatrixFor(TurtleSide.RIGHT);
-
- private static Transformation getMatrixFor(TurtleSide side) {
- var pose = new Matrix4f();
- pose.translate(0.5f, 0.5f, 0.5f);
- pose.rotate(Axis.YN.rotationDegrees(90f));
- pose.rotate(Axis.ZP.rotationDegrees(90f));
- pose.translate(0.0f, 0.0f, side == TurtleSide.RIGHT ? -0.4065f : 0.4065f);
- return new Transformation(pose);
- }
-
- static final TurtleUpgradeModeller UPGRADE_ITEM = new UpgradeItemModeller();
-
- private static final class UpgradeItemModeller implements TurtleUpgradeModeller {
- @Override
- public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
- return TransformedModel.of(upgrade.getUpgradeItem(data), side == TurtleSide.LEFT ? leftTransform : rightTransform);
- }
- }
-}
diff --git a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java b/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java
deleted file mode 100644
index c36fe919b..000000000
--- a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-package dan200.computercraft.impl.client;
-
-import dan200.computercraft.impl.Services;
-import net.minecraft.client.resources.model.BakedModel;
-import net.minecraft.client.resources.model.ModelManager;
-import net.minecraft.resources.ResourceLocation;
-import org.jetbrains.annotations.ApiStatus;
-import org.jspecify.annotations.Nullable;
-
-@ApiStatus.Internal
-public interface ClientPlatformHelper {
- /**
- * Get a model from a resource.
- *
- * @param manager The model manager.
- * @param resourceLocation The model resourceLocation.
- * @return The baked model.
- */
- BakedModel getModel(ModelManager manager, ResourceLocation resourceLocation);
-
- static ClientPlatformHelper get() {
- var instance = Instance.INSTANCE;
- return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
- }
-
- final class Instance {
- static final @Nullable ClientPlatformHelper INSTANCE;
- static final @Nullable Throwable ERROR;
-
- static {
- var helper = Services.tryLoad(ClientPlatformHelper.class);
- INSTANCE = helper.instance();
- ERROR = helper.error();
- }
-
- private Instance() {
- }
- }
-}
diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
index 51988eaac..d15fe4e46 100644
--- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
+++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
@@ -53,13 +53,13 @@ import java.util.function.Function;
*
* Rendering the upgrade
* Next, we need to register a model for our upgrade. This is done by registering a
- * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for your upgrade type.
+ * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModel} for your upgrade type.
*
* Fabric
- * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
+ * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_model}
*
* Forge
- * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
+ * {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_model}
*
* Registering the upgrade itself
* Upgrades themselves are loaded from datapacks when a level is loaded. In order to register our new upgrade, we must
diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java
index 8c3055daf..8b87f0611 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java
@@ -39,7 +39,6 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
-import org.jspecify.annotations.Nullable;
import java.util.function.Consumer;
@@ -137,20 +136,20 @@ public final class ClientHooks {
if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location()));
}
- public static @Nullable BlockState getBlockBreakingState(BlockState state, BlockPos pos) {
+ public static BlockState getBlockBreakingState(BlockState state, BlockPos pos) {
// Only apply to cables which have both a cable and modem
if (state.getBlock() != ModRegistry.Blocks.CABLE.get()
|| !state.getValue(CableBlock.CABLE)
|| state.getValue(CableBlock.MODEM) == CableModemVariant.None
) {
- return null;
+ return state;
}
var hit = Minecraft.getInstance().hitResult;
- if (hit == null || hit.getType() != HitResult.Type.BLOCK) return null;
+ if (hit == null || hit.getType() != HitResult.Type.BLOCK) return state;
var hitPos = ((BlockHitResult) hit).getBlockPos();
- if (!hitPos.equals(pos)) return null;
+ if (!hitPos.equals(pos)) return state;
return WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()))
? state.getBlock().defaultBlockState().setValue(CableBlock.MODEM, state.getValue(CableBlock.MODEM))
diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java
index f9ad68044..cf2b47633 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java
@@ -6,19 +6,21 @@ package dan200.computercraft.client;
import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
-import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
+import dan200.computercraft.api.client.StandaloneModel;
+import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModel;
+import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.item.colour.PocketComputerLight;
import dan200.computercraft.client.item.model.TurtleOverlayModel;
-import dan200.computercraft.client.item.model.TurtleUpgradeModel;
import dan200.computercraft.client.item.properties.PocketComputerStateProperty;
import dan200.computercraft.client.item.properties.TurtleShowElfOverlay;
+import dan200.computercraft.client.platform.ClientPlatformHelper;
+import dan200.computercraft.client.platform.ModelKey;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
-import dan200.computercraft.client.turtle.TurtleModemModeller;
-import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
+import dan200.computercraft.client.turtle.TurtleModemModel;
+import dan200.computercraft.client.turtle.TurtleUpgradeModels;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import dan200.computercraft.shared.turtle.TurtleOverlay;
@@ -32,14 +34,18 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperty;
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
+import net.minecraft.client.resources.model.MissingBlockModel;
+import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
-import java.util.function.Consumer;
/**
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
@@ -53,6 +59,19 @@ public final class ClientRegistry {
private ClientRegistry() {
}
+ private static final Map> models = new ConcurrentHashMap<>();
+
+ public static ModelKey getModel(ResourceLocation model) {
+ return models.computeIfAbsent(model, m -> ClientPlatformHelper.get().createModelKey(m, m::toString));
+ }
+
+ public static StandaloneModel getModel(ModelManager manager, ResourceLocation modelId) {
+ var model = getModel(modelId).get(manager);
+ if (model != null) return model;
+
+ return Objects.requireNonNull(getModel(MissingBlockModel.LOCATION).get(manager));
+ }
+
/**
* Register any client-side objects which don't have to be done on the main thread.
*/
@@ -78,17 +97,17 @@ public final class ClientRegistry {
> void register(MenuType extends M> type, MenuScreens.ScreenConstructor factory);
}
- public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
- register.register(ModRegistry.TurtleUpgradeTypes.SPEAKER.get(), TurtleUpgradeModeller.sided(
+ public static void registerTurtleModels(RegisterTurtleUpgradeModel register) {
+ register.register(ModRegistry.TurtleUpgradeTypes.SPEAKER.get(), TurtleUpgradeModel.sided(
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
));
- register.register(ModRegistry.TurtleUpgradeTypes.WORKBENCH.get(), TurtleUpgradeModeller.sided(
+ register.register(ModRegistry.TurtleUpgradeTypes.WORKBENCH.get(), TurtleUpgradeModel.sided(
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
));
- register.register(ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM.get(), new TurtleModemModeller());
- register.register(ModRegistry.TurtleUpgradeTypes.TOOL.get(), TurtleUpgradeModeller.flatItem());
+ register.register(ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM.get(), TurtleModemModel.UNBAKED);
+ register.register(ModRegistry.TurtleUpgradeTypes.TOOL.get(), TurtleUpgradeModel.flatItem());
}
public static void registerReloadListeners(BiConsumer register, Minecraft minecraft) {
@@ -100,17 +119,22 @@ public final class ClientRegistry {
TurtleBlockEntityRenderer.NORMAL_TURTLE_MODEL,
TurtleBlockEntityRenderer.ADVANCED_TURTLE_MODEL,
TurtleBlockEntityRenderer.COLOUR_TURTLE_MODEL,
+ MissingBlockModel.LOCATION,
};
- public static void registerExtraModels(Consumer register, Collection extraModels) {
- for (var model : EXTRA_MODELS) register.accept(model);
- extraModels.forEach(register);
- TurtleUpgradeModellers.getDependencies().forEach(register);
+ public static void registerExtraModels(
+ BiConsumer, ResourceLocation> registerBasic,
+ BiConsumer>, TurtleUpgradeModel.Unbaked>> registerTurtle,
+ Collection extraModels
+ ) {
+ for (var model : EXTRA_MODELS) registerBasic.accept(getModel(model), model);
+ for (var model : extraModels) registerBasic.accept(getModel(model), model);
+ TurtleUpgradeModels.bake(registerTurtle);
}
public static void registerItemModels(BiConsumer> register) {
register.accept(TurtleOverlayModel.ID, TurtleOverlayModel.CODEC);
- register.accept(TurtleUpgradeModel.ID, TurtleUpgradeModel.CODEC);
+ register.accept(dan200.computercraft.client.item.model.TurtleUpgradeModel.ID, dan200.computercraft.client.item.model.TurtleUpgradeModel.CODEC);
}
public static void registerItemColours(BiConsumer> register) {
diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java
index fa79f4d2f..f5626967b 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/gui/NoTermComputerScreen.java
@@ -72,8 +72,8 @@ public class NoTermComputerScreen extends Screen
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
var direction = scrollHandler.onMouseScroll(scrollX, scrollY);
var inventory = Objects.requireNonNull(minecraft().player).getInventory();
- inventory.setSelectedHotbarSlot(ScrollWheelHandler.getNextScrollWheelSelection(
- direction.y == 0 ? -direction.x : direction.y, inventory.selected, Inventory.getSelectionSize()
+ inventory.setSelectedSlot(ScrollWheelHandler.getNextScrollWheelSelection(
+ direction.y == 0 ? -direction.x : direction.y, inventory.getSelectedSlot(), Inventory.getSelectionSize()
));
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java b/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java
index fc2952a42..85581bb84 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java
@@ -38,7 +38,7 @@ public class IrisShaderMod implements ShaderMod.Provider {
: super.getQuadEmitter(vertexCount, makeBuffer);
}
- private static final class IrisQuadEmitter implements DirectFixedWidthFontRenderer.QuadEmitter {
+ private static final class IrisQuadEmitter extends DirectFixedWidthFontRenderer.QuadEmitter {
private final IrisTextVertexSink sink;
private @Nullable ByteBuffer buffer;
diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java b/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java
index 122f32489..6285229cc 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java
@@ -5,7 +5,6 @@
package dan200.computercraft.client.integration;
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
-import com.mojang.blaze3d.vertex.VertexBuffer;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import java.util.Optional;
@@ -29,7 +28,7 @@ public class ShaderMod {
}
/**
- * Get an appropriate quad emitter for use with {@link VertexBuffer} and {@link DirectFixedWidthFontRenderer} .
+ * Get an appropriate quad emitter for use with a vertex buffer and {@link DirectFixedWidthFontRenderer} .
*
* @param vertexCount The number of vertices.
* @param buffer A function to allocate a temporary buffer.
diff --git a/projects/common/src/client/java/dan200/computercraft/client/item/model/BakedModelWithTransform.java b/projects/common/src/client/java/dan200/computercraft/client/item/model/BakedModelWithTransform.java
deleted file mode 100644
index 091214925..000000000
--- a/projects/common/src/client/java/dan200/computercraft/client/item/model/BakedModelWithTransform.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-package dan200.computercraft.client.item.model;
-
-import net.minecraft.client.renderer.Sheets;
-import net.minecraft.client.renderer.block.model.ItemTransforms;
-import net.minecraft.client.renderer.item.ItemStackRenderState;
-import net.minecraft.client.resources.model.BakedModel;
-import net.minecraft.client.resources.model.DelegateBakedModel;
-
-/**
- * A {@link BakedModel} that wraps another model, but providing different {@link ItemTransforms}.
- */
-class BakedModelWithTransform extends DelegateBakedModel {
- private final ItemTransforms transforms;
-
- BakedModelWithTransform(BakedModel bakedModel, ItemTransforms transforms) {
- super(bakedModel);
- this.transforms = transforms;
- }
-
- static void addLayer(ItemStackRenderState state, BakedModel model, ItemTransforms transforms) {
- state.newLayer().setupBlockModel(new BakedModelWithTransform(model, transforms), Sheets.translucentItemSheet());
- }
-
- @Override
- public ItemTransforms getTransforms() {
- return transforms;
- }
-}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleOverlayModel.java b/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleOverlayModel.java
index 0c658434b..6b748aecb 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleOverlayModel.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleOverlayModel.java
@@ -7,7 +7,7 @@ package dan200.computercraft.client.item.model;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.client.platform.ClientPlatformHelper;
+import dan200.computercraft.client.ClientRegistry;
import dan200.computercraft.shared.turtle.TurtleOverlay;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import net.minecraft.client.Minecraft;
@@ -39,8 +39,9 @@ public record TurtleOverlayModel(ItemTransforms transforms) implements ItemModel
var overlay = TurtleItem.getOverlay(stack);
if (overlay == null) return;
- var model = ClientPlatformHelper.get().getModel(Minecraft.getInstance().getModelManager(), overlay.model());
- BakedModelWithTransform.addLayer(state, model, transforms());
+ var layer = state.newLayer();
+ ClientRegistry.getModel(Minecraft.getInstance().getModelManager(), overlay.model()).setupItemLayer(layer);
+ layer.setTransform(transforms().getTransform(context));
}
public record Unbaked(ResourceLocation base) implements ItemModel.Unbaked {
@@ -51,7 +52,7 @@ public record TurtleOverlayModel(ItemTransforms transforms) implements ItemModel
@Override
public ItemModel bake(BakingContext bakingContext) {
- return new TurtleOverlayModel(bakingContext.bake(base).getTransforms());
+ return new TurtleOverlayModel(bakingContext.blockModelBaker().getModel(base).getTopTransforms());
}
@Override
diff --git a/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleUpgradeModel.java b/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleUpgradeModel.java
index 6d520d60a..6f92b63dd 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleUpgradeModel.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/item/model/TurtleUpgradeModel.java
@@ -4,23 +4,17 @@
package dan200.computercraft.client.item.model;
-import com.mojang.blaze3d.vertex.PoseStack;
-import com.mojang.math.Transformation;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.client.TransformedModel;
-import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.TurtleSide;
-import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
+import dan200.computercraft.client.turtle.TurtleUpgradeModels;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemStackRenderState;
-import net.minecraft.client.renderer.special.SpecialModelRenderer;
-import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
@@ -28,12 +22,12 @@ import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
/**
- * An {@link ItemModel} that renders a turtle upgrade, using its {@link TurtleUpgradeModeller}.
+ * An {@link ItemModel} that renders a turtle upgrade, using its {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModel}.
*
* @param side The side the upgrade resides on.
* @param base The base model. Only used to provide item transforms.
*/
-public record TurtleUpgradeModel(TurtleSide side, BakedModel base) implements ItemModel {
+public record TurtleUpgradeModel(TurtleSide side, ItemTransforms base) implements ItemModel {
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "turtle/upgrade");
public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
TurtleSide.CODEC.fieldOf("side").forGetter(Unbaked::side),
@@ -41,21 +35,11 @@ public record TurtleUpgradeModel(TurtleSide side, BakedModel base) implements It
).apply(instance, Unbaked::new));
@Override
- public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable LivingEntity holder, int light) {
+ public void update(ItemStackRenderState state, ItemStack stack, ItemModelResolver resolver, ItemDisplayContext context, @Nullable ClientLevel level, @Nullable LivingEntity holder, int seed) {
var upgrade = TurtleItem.getUpgradeWithData(stack, side);
if (upgrade == null) return;
- switch (TurtleUpgradeModellers.getModel(upgrade.upgrade(), upgrade.data(), side)) {
- case TransformedModel.Item model -> {
- var childState = new ItemStackRenderState();
- resolver.updateForTopItem(childState, model.stack(), ItemDisplayContext.NONE, false, level, null, 0);
- if (!childState.isEmpty()) {
- state.newLayer().setupSpecialModel(new TransformedRenderer(childState, model.transformation()), null, base);
- }
- }
- case TransformedModel.Baked baked ->
- BakedModelWithTransform.addLayer(state, baked.model(), base.getTransforms());
- }
+ TurtleUpgradeModels.getModeller(upgrade.upgrade()).renderForItem(upgrade.upgrade(), side, upgrade.data(), state, resolver, base.getTransform(context), seed);
}
public record Unbaked(TurtleSide side, ResourceLocation base) implements ItemModel.Unbaked {
@@ -66,29 +50,12 @@ public record TurtleUpgradeModel(TurtleSide side, BakedModel base) implements It
@Override
public ItemModel bake(BakingContext bakingContext) {
- return new TurtleUpgradeModel(side, bakingContext.bake(base));
+ return new TurtleUpgradeModel(side, bakingContext.blockModelBaker().getModel(base).getTopTransforms());
}
@Override
public void resolveDependencies(Resolver resolver) {
- resolver.resolve(base);
- }
- }
-
- private record TransformedRenderer(
- ItemStackRenderState state, Transformation transform
- ) implements SpecialModelRenderer {
- @Override
- public void render(@Nullable Void object, ItemDisplayContext itemDisplayContext, PoseStack poseStack, MultiBufferSource multiBufferSource, int overlay, int light, boolean bl) {
- poseStack.pushPose();
- poseStack.mulPose(transform.getMatrix());
- state.render(poseStack, multiBufferSource, overlay, light);
- poseStack.popPose();
- }
-
- @Override
- public @Nullable Void extractArgument(ItemStack itemStack) {
- return null;
+ resolver.markDependency(base);
}
}
}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/item/properties/PocketComputerStateProperty.java b/projects/common/src/client/java/dan200/computercraft/client/item/properties/PocketComputerStateProperty.java
index 10f88bbd1..c92303f56 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/item/properties/PocketComputerStateProperty.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/item/properties/PocketComputerStateProperty.java
@@ -4,6 +4,7 @@
package dan200.computercraft.client.item.properties;
+import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.pocket.ClientPocketComputers;
@@ -38,6 +39,11 @@ public final class PocketComputerStateProperty implements SelectItemModelPropert
return computer == null ? ComputerState.OFF : computer.getState();
}
+ @Override
+ public Codec valueCodec() {
+ return ComputerState.CODEC;
+ }
+
@Override
public Type extends SelectItemModelProperty, ComputerState> type() {
return TYPE;
diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java
index 03981432b..badf19207 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java
@@ -4,25 +4,40 @@
package dan200.computercraft.client.platform;
-import com.mojang.blaze3d.vertex.PoseStack;
-import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraft.client.resources.model.BakedModel;
+import dan200.computercraft.impl.Services;
+import net.minecraft.client.resources.model.ModelDebugName;
+import net.minecraft.resources.ResourceLocation;
+import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
-public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper {
+public interface ClientPlatformHelper {
static ClientPlatformHelper get() {
- return (ClientPlatformHelper) dan200.computercraft.impl.client.ClientPlatformHelper.get();
+ var instance = Instance.INSTANCE;
+ return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
}
/**
- * Render a {@link BakedModel}, using any loader-specific hooks.
+ * Create a new unique {@link ModelKey}.
*
- * @param transform The current matrix transformation to apply.
- * @param buffers The current pool of render buffers.
- * @param model The model to draw.
- * @param lightmapCoord The current packed lightmap coordinate.
- * @param overlayLight The current overlay light.
- * @param tints Block colour tints to apply to the model.
+ * @param id An identifier for this model key.
+ * @param name The debug name for this model key.
+ * @param The type of baked model.
+ * @return The newly created model key.
*/
- void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, int @Nullable [] tints);
+ @Contract("_, _ -> new")
+ ModelKey createModelKey(ResourceLocation id, ModelDebugName name);
+
+ final class Instance {
+ static final @Nullable ClientPlatformHelper INSTANCE;
+ static final @Nullable Throwable ERROR;
+
+ static {
+ var helper = Services.tryLoad(ClientPlatformHelper.class);
+ INSTANCE = helper.instance();
+ ERROR = helper.error();
+ }
+
+ private Instance() {
+ }
+ }
}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/ModelKey.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ModelKey.java
new file mode 100644
index 000000000..59e993665
--- /dev/null
+++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ModelKey.java
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.client.platform;
+
+import net.minecraft.client.resources.model.ModelManager;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * A key used to identify extra/standalone models.
+ *
+ * @param The type of baked model.
+ */
+public interface ModelKey {
+ /**
+ * Lookup this model key in the model manager.
+ *
+ * @param manager The model manager.
+ * @return The loaded model, or {@code null} if not available.
+ */
+ @Nullable
+ T get(ModelManager manager);
+}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/CustomLecternRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/CustomLecternRenderer.java
index 099cb37d0..5ed4e1a96 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/render/CustomLecternRenderer.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/render/CustomLecternRenderer.java
@@ -19,7 +19,6 @@ import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
-import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.LecternRenderer;
@@ -40,19 +39,16 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
public class CustomLecternRenderer implements BlockEntityRenderer {
private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32;
- private final BlockEntityRenderDispatcher berDispatcher;
private final LecternPrintoutModel printoutModel;
private final LecternPocketModel pocketModel;
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
- berDispatcher = context.getBlockEntityRenderDispatcher();
-
printoutModel = new LecternPrintoutModel();
pocketModel = new LecternPocketModel();
}
@Override
- public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
+ public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay, Vec3 camera) {
poseStack.pushPose();
poseStack.translate(0.5f, 1.0625f, 0.5f);
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
@@ -83,7 +79,7 @@ public class CustomLecternRenderer implements BlockEntityRenderer
- * This is not intended to be used directly, but instead by {@link ClientPlatformHelper#renderBakedModel(PoseStack, MultiBufferSource, BakedModel, int, int, int[])}. The
- * implementation here is identical to {@link ItemRenderer#renderQuadList(PoseStack, VertexConsumer, List, int[], int, int)}.
- *
- * @param transform The current matrix transformation to apply.
- * @param buffer The buffer to draw to.
- * @param quads The quads to draw.
- * @param lightmapCoord The current packed lightmap coordinate.
- * @param overlayLight The current overlay light.
- * @param tints Block colour tints to apply to the model.
- */
- public static void renderQuads(PoseStack transform, VertexConsumer buffer, List quads, int lightmapCoord, int overlayLight, int @Nullable [] tints) {
- var matrix = transform.last();
-
- for (var bakedquad : quads) {
- float r = 1.0f, g = 1.0f, b = 1.0f, a = 1.0f;
- if (tints != null && bakedquad.isTinted()) {
- var idx = bakedquad.getTintIndex();
- if (idx >= 0 && idx < tints.length) {
- var tint = tints[bakedquad.getTintIndex()];
- r = ARGB.red(tint) / 255.0f;
- g = ARGB.green(tint) / 255.0f;
- b = ARGB.blue(tint) / 255.0f;
- a = ARGB.alpha(tint) / 255.0f;
- }
- }
-
- buffer.putBulkData(matrix, bakedquad, r, g, b, a, lightmapCoord, overlayLight);
- }
- }
-}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java
index 9b0de24d8..203986f88 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java
@@ -7,10 +7,9 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.TurtleSide;
-import dan200.computercraft.client.platform.ClientPlatformHelper;
-import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
+import dan200.computercraft.client.ClientRegistry;
+import dan200.computercraft.client.turtle.TurtleUpgradeModels;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.TurtleOverlay;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
@@ -21,14 +20,12 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
-import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import net.minecraft.util.CommonColors;
-import net.minecraft.util.Mth;
-import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
+import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
public class TurtleBlockEntityRenderer implements BlockEntityRenderer {
@@ -45,7 +42,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer {
- transform.mulPose(model.transformation().getMatrix());
- transform.mulPose(Axis.YP.rotation(Mth.PI));
- Minecraft.getInstance().getItemRenderer().renderStatic(
- model.stack(), ItemDisplayContext.FIXED, lightmapCoord, overlayLight, transform, buffers, turtle.getLevel(), 0
- );
- }
-
- case TransformedModel.Baked model ->
- renderModel(transform, buffers, lightmapCoord, overlayLight, model.model(), null);
- }
+ TurtleUpgradeModels.getModeller(upgrade).renderForLevel(upgrade, turtle.getAccess(), side, turtle.getAccess().getUpgradeData(side), transform, buffers, lightmapCoord, overlayLight);
transform.popPose();
@@ -133,21 +119,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer {
/**
@@ -45,7 +48,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer
- DirectFixedWidthFontRenderer.drawTerminalBackground(sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin));
+ DirectFixedWidthFontRenderer.drawTerminalForeground(sink, 0, 0, terminal);
+ var vertexCountAfterForeground = sink.vertexCount();
- renderToBuffer(foregroundBuffer, size + 4, sink -> {
- DirectFixedWidthFontRenderer.drawTerminalForeground(sink, 0, 0, terminal);
- // If the cursor is visible, we append it to the end of our buffer. When rendering, we can either
- // render n or n+1 quads and so toggle the cursor on and off.
- DirectFixedWidthFontRenderer.drawCursor(sink, 0, 0, terminal);
- });
+ DirectFixedWidthFontRenderer.drawCursor(sink, 0, 0, terminal);
+ var vertexCountAfterCursor = sink.vertexCount();
+
+ if (vertexCountAfterCursor > maxVertexCount) {
+ throw new IllegalStateException("Drew too many vertices. Expected " + maxVertexCount + ", drew " + vertexCountAfterCursor);
+ }
+
+ try (var result = backingBufferBuilder.build()) {
+ if (result == null) {
+ // If we have nothing to draw, just mark it as empty. We'll skip drawing in drawWithShader.
+ renderState.indexAfterBackground = renderState.indexAfterForeground = renderState.indexAfterCursor = 0;
+ } else {
+ renderState.register();
+
+ var commandEncoder = RenderSystem.getDevice().createCommandEncoder();
+
+ var resultBuffer = result.byteBuffer();
+ if (resultBuffer.limit() / sink.format().getVertexSize() != vertexCountAfterCursor) {
+ throw new IllegalStateException("Mismatched vertex count");
+ }
+
+ if (renderState.vertexBuffer == null || resultBuffer.limit() > renderState.vertexBuffer.size()) {
+ if (renderState.vertexBuffer != null) {
+ renderState.vertexBuffer.close();
+ renderState.vertexBuffer = null;
+ }
+ renderState.vertexBuffer = RenderSystem.getDevice().createBuffer(
+ () -> "Monitor at " + monitor.getOrigin().getBlockPos(),
+ BufferType.VERTICES, BufferUsage.STATIC_WRITE, resultBuffer
+ );
+ } else if (!renderState.vertexBuffer.isClosed()) {
+ commandEncoder.writeToBuffer(renderState.vertexBuffer, resultBuffer, 0);
+ }
+ }
+ }
+
+ var mode = FixedWidthFontRenderer.TERMINAL_TEXT.mode();
+ renderState.indexAfterBackground = mode.indexCount(vertexCountAfterBackground);
+ renderState.indexAfterForeground = mode.indexCount(vertexCountAfterForeground);
+ renderState.indexAfterCursor = mode.indexCount(vertexCountAfterCursor);
}
+ if (renderState.indexAfterCursor == 0) return;
+
// Our VBO renders coordinates in monitor-space rather than world space. A full sized monitor (8x6) will
// use positions from (0, 0) to (164*FONT_WIDTH, 81*FONT_HEIGHT) = (984, 729). This is far outside the
// normal render distance (~200), and the edges of the monitor fade out due to fog.
@@ -152,69 +192,52 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer draw) {
- var sink = ShaderMod.get().getQuadEmitter(size, backingBufferBuilder);
- draw.accept(sink);
+ private static void drawWithShader(MonitorRenderState renderState, RenderType renderType, int indexOffset, int indexCount) {
+ if (renderState.vertexBuffer == null) {
+ throw new IllegalStateException("MonitorRenderState has not been initialised");
+ }
+ if (indexCount == 0) return;
- var result = backingBufferBuilder.build();
- if (result == null) {
- // If we have nothing to draw, just mark it as empty. We'll skip drawing in drawWithShader.
- vbo.indexCount = 0;
- return;
+ renderType.setupRenderState();
+
+ var autoStorageBuffer = RenderSystem.getSequentialBuffer(renderType.mode());
+ var indexBuffer = autoStorageBuffer.getBuffer(indexOffset + indexCount);
+
+ try (var renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(
+ renderType.getRenderTarget().getColorTexture(), OptionalInt.empty(), renderType.getRenderTarget().getDepthTexture(), OptionalDouble.empty()
+ )) {
+ renderPass.setPipeline(renderType.getRenderPipeline());
+ renderPass.setVertexBuffer(0, renderState.vertexBuffer);
+ renderPass.setIndexBuffer(indexBuffer, autoStorageBuffer.type());
+
+ for (var j = 0; j < 12; j++) {
+ var gpuTexture = RenderSystem.getShaderTexture(j);
+ if (gpuTexture != null) renderPass.bindSampler("Sampler" + j, gpuTexture);
+ }
+
+ renderPass.drawIndexed(indexOffset, indexCount);
}
- var buffer = result.byteBuffer();
- var vertices = buffer.limit() / sink.format().getVertexSize();
-
- vbo.bind();
- vbo.upload(new MeshData(result, new MeshData.DrawState(
- sink.format(),
- vertices, FixedWidthFontRenderer.TERMINAL_TEXT.mode().indexCount(vertices),
- FixedWidthFontRenderer.TERMINAL_TEXT.mode(), VertexFormat.IndexType.least(vertices)
- )));
- }
-
- private static void drawWithShader(VertexBuffer buffer, Matrix4f modelView, Matrix4f projection, @Nullable CompiledShaderProgram compiledShaderProgram, int indicies) {
- var originalIndexCount = buffer.indexCount;
- if (originalIndexCount == 0) return;
-
- try {
- buffer.indexCount = indicies;
- buffer.drawWithShader(modelView, projection, compiledShaderProgram);
- } finally {
- buffer.indexCount = originalIndexCount;
- }
+ renderType.clearRenderState();
}
@Override
diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorRenderState.java b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorRenderState.java
index a94f28817..d771161ea 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorRenderState.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorRenderState.java
@@ -5,8 +5,7 @@
package dan200.computercraft.client.render.monitor;
import com.google.errorprone.annotations.concurrent.GuardedBy;
-import com.mojang.blaze3d.buffers.BufferUsage;
-import com.mojang.blaze3d.vertex.VertexBuffer;
+import com.mojang.blaze3d.buffers.GpuBuffer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import net.minecraft.core.BlockPos;
import org.jspecify.annotations.Nullable;
@@ -21,53 +20,39 @@ import java.util.Set;
* This is automatically cleared by {@link dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity} when the
* entity is unloaded on the client side (see {@link MonitorRenderState#close()}).
*/
-public class MonitorRenderState implements ClientMonitor.RenderState {
+public final class MonitorRenderState implements ClientMonitor.RenderState {
@GuardedBy("allMonitors")
private static final Set allMonitors = new HashSet<>();
- public long lastRenderFrame = -1;
- public @Nullable BlockPos lastRenderPos = null;
+ long lastRenderFrame = -1;
+ @Nullable
+ BlockPos lastRenderPos = null;
- public @Nullable VertexBuffer backgroundBuffer;
- public @Nullable VertexBuffer foregroundBuffer;
+ @Nullable
+ GpuBuffer vertexBuffer;
- /**
- * Create the appropriate buffer if needed.
- *
- * @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer,
- * or this mode does not require one.
- */
- public boolean createBuffer() {
- if (backgroundBuffer != null) return false;
+ int indexAfterBackground;
+ int indexAfterForeground;
+ int indexAfterCursor;
- deleteBuffers();
- backgroundBuffer = new VertexBuffer(BufferUsage.STATIC_WRITE);
- foregroundBuffer = new VertexBuffer(BufferUsage.STATIC_WRITE);
- addMonitor();
- return true;
- }
+ void register() {
+ if (vertexBuffer != null) return;
- private void addMonitor() {
synchronized (allMonitors) {
allMonitors.add(this);
}
}
private void deleteBuffers() {
- if (backgroundBuffer != null) {
- backgroundBuffer.close();
- backgroundBuffer = null;
- }
-
- if (foregroundBuffer != null) {
- foregroundBuffer.close();
- foregroundBuffer = null;
+ if (vertexBuffer != null) {
+ vertexBuffer.close();
+ vertexBuffer = null;
}
}
@Override
public void close() {
- if (backgroundBuffer != null) {
+ if (vertexBuffer != null) {
synchronized (allMonitors) {
allMonitors.remove(this);
}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java
index 6bb344868..50c865d88 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java
@@ -33,7 +33,7 @@ import static org.lwjgl.system.MemoryUtil.*;
*
* Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate,
* it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}.
- *
+ *
* IMPORTANT: When making changes to this class, please check if you need to make the same changes to
* {@link FixedWidthFontRenderer}.
*/
@@ -156,21 +156,30 @@ public final class DirectFixedWidthFontRenderer {
}
}
- public static int getVertexCount(Terminal terminal) {
- return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
- }
-
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
+ buffer.vertexCount += 4;
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
}
- public interface QuadEmitter {
- VertexFormat format();
+ public abstract static class QuadEmitter {
+ private int vertexCount;
- void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
+ public abstract VertexFormat format();
+
+ protected abstract void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
+
+ public int vertexCount() {
+ return vertexCount;
+ }
}
- public record ByteBufferEmitter(ByteBufferBuilder builder) implements QuadEmitter {
+ public static final class ByteBufferEmitter extends QuadEmitter {
+ private final ByteBufferBuilder builder;
+
+ public ByteBufferEmitter(ByteBufferBuilder builder) {
+ this.builder = builder;
+ }
+
@Override
public VertexFormat format() {
return TERMINAL_TEXT.format();
diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java
index 02ea2a86f..fd36ae8ae 100644
--- a/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java
+++ b/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java
@@ -40,6 +40,8 @@ public final class FixedWidthFontRenderer {
*/
public static final RenderType TERMINAL_TEXT = RenderType.text(FONT);
+ public static final RenderType TERMINAL_TEXT_OFFSET = RenderType.textPolygonOffset(FONT);
+
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
static final float WIDTH = 256.0f;
diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModel.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModel.java
new file mode 100644
index 000000000..6735dc7b6
--- /dev/null
+++ b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModel.java
@@ -0,0 +1,89 @@
+// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.client.turtle;
+
+import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.client.StandaloneModel;
+import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
+import dan200.computercraft.api.client.turtle.TurtleUpgradeModelViaStandalone;
+import dan200.computercraft.api.turtle.TurtleSide;
+import dan200.computercraft.shared.ModRegistry;
+import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
+import dan200.computercraft.shared.util.DataComponentUtil;
+import net.minecraft.client.resources.model.ModelBaker;
+import net.minecraft.core.component.DataComponentPatch;
+import net.minecraft.resources.ResourceLocation;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A {@link TurtleUpgradeModel} for modems, providing different models depending on if the modem is on/off.
+ *
+ * @param normal The models for a normal wireless modem.
+ * @param advanced The models for an advanced/ender modem.
+ */
+public record TurtleModemModel(
+ ModemModels normal, ModemModels advanced
+) implements TurtleUpgradeModelViaStandalone {
+ public static final TurtleUpgradeModel.Unbaked UNBAKED = new Unbaked();
+
+ @Override
+ public StandaloneModel getModel(TurtleModem modem, TurtleSide side, DataComponentPatch data) {
+ var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x);
+
+ var models = modem.advanced() ? advanced() : normal();
+ return side == TurtleSide.LEFT
+ ? (active ? models.leftOnModel() : models.leftOffModel())
+ : (active ? models.rightOnModel() : models.rightOffModel());
+ }
+
+ private static final class Unbaked implements TurtleUpgradeModel.Unbaked {
+ @Override
+ public TurtleUpgradeModel bake(ModelBaker baker) {
+ return new TurtleModemModel(
+ ModemModels.NORMAL.map(x -> StandaloneModel.of(x, baker)),
+ ModemModels.ADVANCED.map(x -> StandaloneModel.of(x, baker))
+ );
+ }
+
+ @Override
+ public void resolveDependencies(Resolver resolver) {
+ ModemModels.NORMAL.forEach(resolver::markDependency);
+ ModemModels.ADVANCED.forEach(resolver::markDependency);
+ }
+ }
+
+ private record ModemModels(
+ T leftOffModel, T rightOffModel,
+ T leftOnModel, T rightOnModel
+ ) {
+ private static final ModemModels NORMAL = create("normal");
+ private static final ModemModels ADVANCED = create("advanced");
+
+ static ModemModels create(String type) {
+ return new ModemModels<>(
+ ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_left"),
+ ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_right"),
+ ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_left"),
+ ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_right")
+ );
+ }
+
+ public void forEach(Consumer out) {
+ out.accept(leftOffModel());
+ out.accept(rightOffModel);
+ out.accept(leftOnModel());
+ out.accept(rightOnModel());
+ }
+
+ public ModemModels map(Function mapper) {
+ return new ModemModels<>(
+ mapper.apply(leftOffModel()), mapper.apply(rightOffModel()),
+ mapper.apply(leftOnModel()), mapper.apply(rightOnModel())
+ );
+ }
+ }
+}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java
deleted file mode 100644
index 141e42572..000000000
--- a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
-//
-// SPDX-License-Identifier: LicenseRef-CCPL
-
-package dan200.computercraft.client.turtle;
-
-import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.client.TransformedModel;
-import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
-import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.api.turtle.TurtleSide;
-import dan200.computercraft.shared.ModRegistry;
-import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
-import dan200.computercraft.shared.util.DataComponentUtil;
-import net.minecraft.core.component.DataComponentPatch;
-import net.minecraft.resources.ResourceLocation;
-import org.jspecify.annotations.Nullable;
-
-import java.util.stream.Stream;
-
-/**
- * A {@link TurtleUpgradeModeller} for modems, providing different models depending on if the modem is on/off.
- */
-public class TurtleModemModeller implements TurtleUpgradeModeller {
- @Override
- public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
- var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x);
-
- var models = upgrade.advanced() ? ModemModels.ADVANCED : ModemModels.NORMAL;
- return side == TurtleSide.LEFT
- ? TransformedModel.of(active ? models.leftOnModel() : models.leftOffModel())
- : TransformedModel.of(active ? models.rightOnModel() : models.rightOffModel());
- }
-
- @Override
- public Stream getDependencies() {
- return Stream.of(ModemModels.NORMAL, ModemModels.ADVANCED).flatMap(ModemModels::getDependencies);
- }
-
- private record ModemModels(
- ResourceLocation leftOffModel, ResourceLocation rightOffModel,
- ResourceLocation leftOnModel, ResourceLocation rightOnModel
- ) {
- private static final ModemModels NORMAL = create("normal");
- private static final ModemModels ADVANCED = create("advanced");
-
- public static ModemModels create(String type) {
- return new ModemModels(
- ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_left"),
- ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_off_right"),
- ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_left"),
- ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_modem_" + type + "_on_right")
- );
- }
-
- public Stream getDependencies() {
- return Stream.of(leftOffModel, rightOffModel, leftOnModel, rightOnModel);
- }
- }
-}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java
deleted file mode 100644
index 6d974549a..000000000
--- a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
-//
-// SPDX-License-Identifier: MPL-2.0
-
-package dan200.computercraft.client.turtle;
-
-import dan200.computercraft.api.client.TransformedModel;
-import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
-import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.api.turtle.ITurtleUpgrade;
-import dan200.computercraft.api.turtle.TurtleSide;
-import dan200.computercraft.api.upgrades.UpgradeType;
-import dan200.computercraft.shared.util.RegistryHelper;
-import net.minecraft.client.Minecraft;
-import net.minecraft.core.component.DataComponentPatch;
-import net.minecraft.resources.ResourceLocation;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Stream;
-
-/**
- * A registry of {@link TurtleUpgradeModeller}s.
- */
-public final class TurtleUpgradeModellers {
- private static final TurtleUpgradeModeller NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) ->
- TransformedModel.of(Minecraft.getInstance().getModelManager().getMissingModel());
-
- private static final Map, TurtleUpgradeModeller>> turtleModels = new ConcurrentHashMap<>();
- private static volatile boolean fetchedModels;
-
- private TurtleUpgradeModellers() {
- }
-
- public static void register(UpgradeType type, TurtleUpgradeModeller modeller) {
- if (fetchedModels) {
- throw new IllegalStateException(String.format(
- "Turtle upgrade type %s must be registered before models are baked.",
- RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), type)
- ));
- }
-
- if (turtleModels.putIfAbsent(type, modeller) != null) {
- throw new IllegalStateException("Modeller already registered for serialiser");
- }
- }
-
- public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
- return getModeller(upgrade).getModel(upgrade, access, side, access.getUpgradeData(side));
- }
-
- public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
- return getModeller(upgrade).getModel(upgrade, null, side, data);
- }
-
- @SuppressWarnings("unchecked")
- private static TurtleUpgradeModeller getModeller(T upgrade) {
- var modeller = turtleModels.get(upgrade.getType());
- return (TurtleUpgradeModeller) (modeller == null ? NULL_TURTLE_MODELLER : modeller);
- }
-
- public static Stream getDependencies() {
- fetchedModels = true;
- return turtleModels.values().stream().flatMap(TurtleUpgradeModeller::getDependencies);
- }
-}
diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModels.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModels.java
new file mode 100644
index 000000000..b231f09ed
--- /dev/null
+++ b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModels.java
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.client.turtle;
+
+import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
+import dan200.computercraft.api.turtle.ITurtleUpgrade;
+import dan200.computercraft.api.upgrades.UpgradeType;
+import dan200.computercraft.client.platform.ClientPlatformHelper;
+import dan200.computercraft.client.platform.ModelKey;
+import dan200.computercraft.shared.util.RegistryHelper;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.resources.model.MissingBlockModel;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+
+/**
+ * A registry of {@link TurtleUpgradeModel}s.
+ */
+public final class TurtleUpgradeModels {
+ private static final Object fetchedLock = new Object();
+ private static volatile boolean fetchedModels;
+
+ private static final Map>, TurtleUpgradeModel.Unbaked>> unbaked = new ConcurrentHashMap<>();
+ private static final Map, ModelKey extends TurtleUpgradeModel>>> modelKeys = new ConcurrentHashMap<>();
+ public static final ModelKey> missingModelKey = ClientPlatformHelper.get().createModelKey(
+ MissingBlockModel.LOCATION,
+ () -> "Missing turtle model"
+ );
+
+ private TurtleUpgradeModels() {
+ }
+
+ public static void register(UpgradeType type, TurtleUpgradeModel.Unbaked super T> modeller) {
+ if (fetchedModels) {
+ throw new IllegalStateException(String.format(
+ "Turtle upgrade type %s must be registered before models are baked.",
+ RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), type)
+ ));
+ }
+
+ if (unbaked.putIfAbsent(getModelKey(type), modeller) != null) {
+ throw new IllegalStateException("Modeller already registered for serialiser");
+ }
+ }
+
+ public static void fetch(Runnable action) {
+ if (fetchedModels) return;
+ synchronized (fetchedLock) {
+ if (fetchedModels) return;
+ action.run();
+ fetchedModels = true;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static ModelKey> getModelKey(UpgradeType type) {
+ return (ModelKey>) modelKeys.computeIfAbsent(type, t -> {
+ var id = RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), t);
+ return ClientPlatformHelper.get().createModelKey(
+ RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), t),
+ () -> "Turtle upgrade " + id
+ );
+ });
+ }
+
+ public static TurtleUpgradeModel super T> getModeller(T upgrade) {
+ var modelManager = Minecraft.getInstance().getModelManager();
+
+ @SuppressWarnings("unchecked")
+ var model = getModelKey((UpgradeType) upgrade.getType()).get(modelManager);
+ if (model != null) return model;
+
+ var missing = missingModelKey.get(modelManager);
+ if (missing == null) throw new IllegalStateException("Rendering turtles before models are baked");
+ return missing;
+ }
+
+ public static void bake(BiConsumer>, TurtleUpgradeModel.Unbaked>> baker) {
+ unbaked.forEach(baker);
+ baker.accept(missingModelKey, TurtleUpgradeModel.sided(MissingBlockModel.LOCATION, MissingBlockModel.LOCATION));
+ }
+}
diff --git a/projects/common/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java b/projects/common/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java
new file mode 100644
index 000000000..08fbc8887
--- /dev/null
+++ b/projects/common/src/client/java/dan200/computercraft/mixin/client/BlockRenderDispatcherMixin.java
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
+//
+// SPDX-License-Identifier: MPL-2.0
+
+package dan200.computercraft.mixin.client;
+
+import com.llamalad7.mixinextras.sugar.Local;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import dan200.computercraft.client.ClientHooks;
+import net.minecraft.client.renderer.block.BlockRenderDispatcher;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.BlockAndTintGetter;
+import net.minecraft.world.level.block.state.BlockState;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+/**
+ * Provides custom block breaking progress for modems, so it only applies to the current part.
+ *
+ * @see BlockRenderDispatcher#renderBreakingTexture(BlockState, BlockPos, BlockAndTintGetter, PoseStack, VertexConsumer)
+ */
+@Mixin(BlockRenderDispatcher.class)
+public class BlockRenderDispatcherMixin {
+ @ModifyVariable(method = "renderBreakingTexture", at = @At("HEAD"))
+ public BlockState renderBlockDamage(BlockState state, @Local BlockPos pos) {
+ return ClientHooks.getBlockBreakingState(state, pos);
+ }
+}
diff --git a/projects/forge/src/client/resources/computercraft-client.forge.mixins.json b/projects/common/src/client/resources/computercraft-client.mixins.json
similarity index 100%
rename from projects/forge/src/client/resources/computercraft-client.forge.mixins.json
rename to projects/common/src/client/resources/computercraft-client.mixins.json
diff --git a/projects/common/src/datagen/java/dan200/computercraft/data/client/BlockModelProvider.java b/projects/common/src/datagen/java/dan200/computercraft/data/client/BlockModelProvider.java
index 792dac94e..27a264770 100644
--- a/projects/common/src/datagen/java/dan200/computercraft/data/client/BlockModelProvider.java
+++ b/projects/common/src/datagen/java/dan200/computercraft/data/client/BlockModelProvider.java
@@ -4,6 +4,7 @@
package dan200.computercraft.data.client;
+import com.mojang.math.Quadrant;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.item.model.TurtleOverlayModel;
@@ -24,8 +25,13 @@ import dan200.computercraft.shared.turtle.TurtleOverlay;
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.data.models.BlockModelGenerators;
-import net.minecraft.client.data.models.blockstates.*;
+import net.minecraft.client.data.models.MultiVariant;
+import net.minecraft.client.data.models.blockstates.ConditionBuilder;
+import net.minecraft.client.data.models.blockstates.MultiPartGenerator;
+import net.minecraft.client.data.models.blockstates.MultiVariantGenerator;
+import net.minecraft.client.data.models.blockstates.PropertyDispatch;
import net.minecraft.client.data.models.model.*;
+import net.minecraft.client.renderer.block.model.VariantMutator;
import net.minecraft.client.renderer.item.EmptyModel;
import net.minecraft.client.renderer.item.properties.conditional.HasComponent;
import net.minecraft.core.Direction;
@@ -33,7 +39,6 @@ import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
-import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.Property;
@@ -43,6 +48,7 @@ import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
+import static net.minecraft.client.data.models.BlockModelGenerators.*;
import static net.minecraft.client.data.models.model.ModelLocationUtils.getModelLocation;
import static net.minecraft.client.data.models.model.TextureMapping.getBlockTexture;
@@ -116,15 +122,14 @@ public class BlockModelProvider {
registerTurtleModem(generators, "block/turtle_modem_advanced", "block/wireless_modem_advanced_face");
generators.blockStateOutput.accept(
- BlockModelGenerators.createSimpleBlock(ModRegistry.Blocks.LECTERN.get(), getModelLocation(Blocks.LECTERN))
+ createSimpleBlock(ModRegistry.Blocks.LECTERN.get(), plainVariant(getModelLocation(Blocks.LECTERN)))
.with(createHorizontalFacingDispatch())
);
}
private static void registerDiskDrive(BlockModelGenerators generators) {
var diskDrive = ModRegistry.Blocks.DISK_DRIVE.get();
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(diskDrive)
- .with(createHorizontalFacingDispatch())
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(diskDrive)
.with(createModelDispatch(DiskDriveBlock.STATE, value -> {
var textureSuffix = switch (value) {
case EMPTY -> "_front";
@@ -137,14 +142,14 @@ public class BlockModelProvider {
generators.modelOutput
);
}))
+ .with(createHorizontalFacingDispatch())
);
generators.registerSimpleItemModel(diskDrive, getModelLocation(diskDrive, "_empty"));
}
private static void registerPrinter(BlockModelGenerators generators) {
var printer = ModRegistry.Blocks.PRINTER.get();
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(printer)
- .with(createHorizontalFacingDispatch())
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(printer)
.with(createModelDispatch(PrinterBlock.TOP, PrinterBlock.BOTTOM, (top, bottom) -> {
String model, texture;
if (top && bottom) {
@@ -165,13 +170,13 @@ public class BlockModelProvider {
generators.modelOutput
);
}))
+ .with(createHorizontalFacingDispatch())
);
generators.registerSimpleItemModel(printer, getModelLocation(printer, "_empty"));
}
private static void registerComputer(BlockModelGenerators generators, ComputerBlock> block) {
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block)
- .with(createHorizontalFacingDispatch())
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(block)
.with(createModelDispatch(ComputerBlock.STATE, state -> switch (state) {
case OFF -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix(
block, "_" + state.getSerializedName(),
@@ -184,6 +189,7 @@ public class BlockModelProvider {
generators.modelOutput
);
}))
+ .with(createHorizontalFacingDispatch())
);
generators.registerSimpleItemModel(block, getModelLocation(block, "_blinking"));
}
@@ -193,7 +199,7 @@ public class BlockModelProvider {
var particleModel = ModelTemplates.PARTICLE_ONLY.createWithSuffix(
block, "_particle", TextureMapping.particle(getBlockTexture(block, "_front")), generators.modelOutput
);
- generators.blockStateOutput.accept(BlockModelGenerators.createSimpleBlock(block, particleModel));
+ generators.blockStateOutput.accept(createSimpleBlock(block, plainVariant(particleModel)));
// We then register the full model for use in items and the BE renderer.
var model = TURTLE.create(block, new TextureMapping()
@@ -224,17 +230,17 @@ public class BlockModelProvider {
}
private static void registerWirelessModem(BlockModelGenerators generators, WirelessModemBlock block) {
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block)
- .with(createFacingDispatch())
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(block)
.with(createModelDispatch(WirelessModemBlock.ON,
on -> modemModel(generators, getModelLocation(block, on ? "_on" : "_off"), getBlockTexture(block, "_face" + (on ? "_on" : "")))
- )));
+ ))
+ .with(createFacingDispatch()));
generators.registerSimpleItemModel(block, getModelLocation(block, "_off"));
}
private static void registerWiredModems(BlockModelGenerators generators) {
var fullBlock = ModRegistry.Blocks.WIRED_MODEM_FULL.get();
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(fullBlock)
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(fullBlock)
.with(createModelDispatch(WiredModemFullBlock.MODEM_ON, WiredModemFullBlock.PERIPHERAL_ON, (on, peripheral) -> {
var suffix = (on ? "_on" : "_off") + (peripheral ? "_peripheral" : "");
var faceTexture = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/wired_modem_face" + (peripheral ? "_peripheral" : "") + (on ? "_on" : ""));
@@ -281,10 +287,10 @@ public class BlockModelProvider {
monitorModel(generators, block, "_u", 22, 5, 0, 38);
monitorModel(generators, block, "_ud", 21, 6, 0, 37);
- generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block)
+ generators.blockStateOutput.accept(MultiVariantGenerator.dispatch(block)
+ .with(createModelDispatch(MonitorBlock.STATE, edge -> getModelLocation(block, edge == MonitorEdgeState.NONE ? "" : "_" + edge.getSerializedName())))
.with(createHorizontalFacingDispatch())
.with(createVerticalFacingDispatch(MonitorBlock.ORIENTATION))
- .with(createModelDispatch(MonitorBlock.STATE, edge -> getModelLocation(block, edge == MonitorEdgeState.NONE ? "" : "_" + edge.getSerializedName())))
);
generators.registerSimpleItemModel(block, monitorModel(generators, block, "_item", 15, 4, 0, 32));
}
@@ -308,55 +314,54 @@ public class BlockModelProvider {
var coreFacing = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/cable_core_facing");
// Up/Down
generator.with(
- Condition.or(
+ or(
cableNoNeighbour(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).term(CableBlock.UP, true),
cableNoNeighbour(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).term(CableBlock.DOWN, true)
),
- Variant.variant().with(VariantProperties.MODEL, coreFacing).with(VariantProperties.X_ROT, VariantProperties.Rotation.R90)
+ plainVariant(coreFacing).with(VariantMutator.X_ROT.withValue(Quadrant.R90))
);
// North/South and no neighbours
generator.with(
- Condition.or(
+ or(
cableNoNeighbour(Direction.UP, Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST),
cableNoNeighbour(Direction.UP, Direction.DOWN, Direction.EAST, Direction.WEST).term(CableBlock.NORTH, true),
cableNoNeighbour(Direction.UP, Direction.DOWN, Direction.EAST, Direction.WEST).term(CableBlock.SOUTH, true)
),
- Variant.variant().with(VariantProperties.MODEL, coreFacing).with(VariantProperties.Y_ROT, VariantProperties.Rotation.R0)
+ plainVariant(coreFacing).with(VariantMutator.Y_ROT.withValue(Quadrant.R0))
);
// East/West
generator.with(
- Condition.or(
+ or(
cableNoNeighbour(Direction.NORTH, Direction.SOUTH, Direction.UP, Direction.DOWN).term(CableBlock.EAST, true),
cableNoNeighbour(Direction.NORTH, Direction.SOUTH, Direction.UP, Direction.DOWN).term(CableBlock.WEST, true)
),
- Variant.variant().with(VariantProperties.MODEL, coreFacing).with(VariantProperties.Y_ROT, VariantProperties.Rotation.R90)
+ plainVariant(coreFacing).with(VariantMutator.Y_ROT.withValue(Quadrant.R90))
);
// Find all other possibilities and emit a "solid" core which doesn't have a facing direction.
var core = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/cable_core_any");
- List rightAngles = new ArrayList<>();
+ List rightAngles = new ArrayList<>();
for (var i = 0; i < DirectionUtil.FACINGS.length; i++) {
for (var j = i; j < DirectionUtil.FACINGS.length; j++) {
if (DirectionUtil.FACINGS[i].getAxis() == DirectionUtil.FACINGS[j].getAxis()) continue;
- rightAngles.add(new Condition.TerminalCondition()
+ rightAngles.add(condition()
.term(CableBlock.CABLE, true).term(CABLE_DIRECTIONS[i], true).term(CABLE_DIRECTIONS[j], true)
);
}
}
- generator.with(Condition.or(rightAngles.toArray(new Condition[0])), Variant.variant().with(VariantProperties.MODEL, core));
+ generator.with(or(rightAngles.toArray(new ConditionBuilder[0])), plainVariant(core));
// Then emit the actual cable arms
var arm = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/cable_arm");
for (var direction : DirectionUtil.FACINGS) {
generator.with(
- new Condition.TerminalCondition().term(CABLE_DIRECTIONS[direction.ordinal()], true),
- Variant.variant()
- .with(VariantProperties.MODEL, arm)
- .with(VariantProperties.X_ROT, toXAngle(direction.getOpposite()))
- .with(VariantProperties.Y_ROT, toYAngle(direction.getOpposite()))
+ condition().term(CABLE_DIRECTIONS[direction.ordinal()], true),
+ plainVariant(arm)
+ .with(VariantMutator.X_ROT.withValue(toXAngle(direction.getOpposite())))
+ .with(VariantMutator.Y_ROT.withValue(toYAngle(direction.getOpposite())))
);
}
@@ -366,11 +371,10 @@ public class BlockModelProvider {
for (var peripheral : BOOLEANS) {
var suffix = (on ? "_on" : "_off") + (peripheral ? "_peripheral" : "");
generator.with(
- new Condition.TerminalCondition().term(CableBlock.MODEM, CableModemVariant.from(direction, on, peripheral)),
- Variant.variant()
- .with(VariantProperties.MODEL, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/wired_modem" + suffix))
- .with(VariantProperties.X_ROT, toXAngle(direction))
- .with(VariantProperties.Y_ROT, toYAngle(direction))
+ condition().term(CableBlock.MODEM, CableModemVariant.from(direction, on, peripheral)),
+ plainVariant(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/wired_modem" + suffix))
+ .with(VariantMutator.X_ROT.withValue(toXAngle(direction.getOpposite())))
+ .with(VariantMutator.Y_ROT.withValue(toYAngle(direction.getOpposite())))
);
}
}
@@ -390,8 +394,8 @@ public class BlockModelProvider {
private static final BooleanProperty[] CABLE_DIRECTIONS = { CableBlock.DOWN, CableBlock.UP, CableBlock.NORTH, CableBlock.SOUTH, CableBlock.WEST, CableBlock.EAST };
private static final boolean[] BOOLEANS = new boolean[]{ false, true };
- private static Condition.TerminalCondition cableNoNeighbour(Direction... directions) {
- var condition = new Condition.TerminalCondition().term(CableBlock.CABLE, true);
+ private static ConditionBuilder cableNoNeighbour(Direction... directions) {
+ var condition = condition().term(CableBlock.CABLE, true);
for (var direction : directions) condition.term(CABLE_DIRECTIONS[direction.ordinal()], false);
return condition;
}
@@ -418,66 +422,60 @@ public class BlockModelProvider {
generators.registerSimpleItemModel(block, ModelLocationUtils.getModelLocation(block));
}
- private static VariantProperties.Rotation toXAngle(Direction direction) {
+ private static Quadrant toXAngle(Direction direction) {
return switch (direction) {
- default -> VariantProperties.Rotation.R0;
- case UP -> VariantProperties.Rotation.R270;
- case DOWN -> VariantProperties.Rotation.R90;
+ default -> Quadrant.R0;
+ case UP -> Quadrant.R270;
+ case DOWN -> Quadrant.R90;
};
}
- private static VariantProperties.Rotation toYAngle(Direction direction) {
+ private static Quadrant toYAngle(Direction direction) {
return switch (direction) {
- default -> VariantProperties.Rotation.R0;
- case NORTH -> VariantProperties.Rotation.R0;
- case SOUTH -> VariantProperties.Rotation.R180;
- case EAST -> VariantProperties.Rotation.R90;
- case WEST -> VariantProperties.Rotation.R270;
+ default -> Quadrant.R0;
+ case NORTH -> Quadrant.R0;
+ case SOUTH -> Quadrant.R180;
+ case EAST -> Quadrant.R90;
+ case WEST -> Quadrant.R270;
};
}
- private static PropertyDispatch createHorizontalFacingDispatch() {
- var dispatch = PropertyDispatch.property(BlockStateProperties.HORIZONTAL_FACING);
+ private static PropertyDispatch createHorizontalFacingDispatch() {
+ /*var dispatch = PropertyDispatch.modify(BlockStateProperties.HORIZONTAL_FACING);
for (var direction : BlockStateProperties.HORIZONTAL_FACING.getPossibleValues()) {
dispatch.select(direction, Variant.variant().with(VariantProperties.Y_ROT, toYAngle(direction)));
}
- return dispatch;
+ return dispatch;*/
+ return BlockModelGenerators.ROTATION_HORIZONTAL_FACING;
}
- private static PropertyDispatch createVerticalFacingDispatch(Property property) {
- var dispatch = PropertyDispatch.property(property);
+ private static PropertyDispatch createVerticalFacingDispatch(Property property) {
+ var dispatch = PropertyDispatch.modify(property);
for (var direction : property.getPossibleValues()) {
- dispatch.select(direction, Variant.variant().with(VariantProperties.X_ROT, toXAngle(direction)));
+ dispatch.select(direction, VariantMutator.X_ROT.withValue(toXAngle(direction)));
}
return dispatch;
}
- private static PropertyDispatch createFacingDispatch() {
- var dispatch = PropertyDispatch.property(BlockStateProperties.FACING);
- for (var direction : BlockStateProperties.FACING.getPossibleValues()) {
- dispatch.select(direction, Variant.variant()
- .with(VariantProperties.Y_ROT, toYAngle(direction))
- .with(VariantProperties.X_ROT, toXAngle(direction))
- );
- }
- return dispatch;
+ private static PropertyDispatch createFacingDispatch() {
+ return BlockModelGenerators.ROTATION_FACING;
}
- private static > PropertyDispatch createModelDispatch(Property property, Function makeModel) {
- var variant = PropertyDispatch.property(property);
+ private static > PropertyDispatch createModelDispatch(Property property, Function makeModel) {
+ var variant = PropertyDispatch.initial(property);
for (var value : property.getPossibleValues()) {
- variant.select(value, Variant.variant().with(VariantProperties.MODEL, makeModel.apply(value)));
+ variant.select(value, plainVariant(makeModel.apply(value)));
}
return variant;
}
- private static , U extends Comparable> PropertyDispatch createModelDispatch(
+ private static , U extends Comparable> PropertyDispatch createModelDispatch(
Property propertyT, Property propertyU, BiFunction makeModel
) {
- var variant = PropertyDispatch.properties(propertyT, propertyU);
+ var variant = PropertyDispatch.initial(propertyT, propertyU);
for (var valueT : propertyT.getPossibleValues()) {
for (var valueU : propertyU.getPossibleValues()) {
- variant.select(valueT, valueU, Variant.variant().with(VariantProperties.MODEL, makeModel.apply(valueT, valueU)));
+ variant.select(valueT, valueU, plainVariant(makeModel.apply(valueT, valueU)));
}
}
return variant;
diff --git a/projects/common/src/examples/java/com/example/examplemod/peripheral/BrewingStandPeripheral.java b/projects/common/src/examples/java/com/example/examplemod/peripheral/BrewingStandPeripheral.java
index 54a32131d..b9e5dc1c4 100644
--- a/projects/common/src/examples/java/com/example/examplemod/peripheral/BrewingStandPeripheral.java
+++ b/projects/common/src/examples/java/com/example/examplemod/peripheral/BrewingStandPeripheral.java
@@ -28,7 +28,7 @@ public class BrewingStandPeripheral implements IPeripheral {
@LuaFunction
public final int getFuel() {
// Don't do it this way! Use an access widener/transformer to access the "fuel" field instead.
- return brewingStand.saveWithoutMetadata(brewingStand.getLevel().registryAccess()).getInt("Fuel");
+ return brewingStand.saveWithoutMetadata(brewingStand.getLevel().registryAccess()).getByteOr("Fuel", (byte) 0);
}
@Override
diff --git a/projects/common/src/examples/java/com/example/examplemod/peripheral/FurnacePeripheral.java b/projects/common/src/examples/java/com/example/examplemod/peripheral/FurnacePeripheral.java
index e09ba7f1c..75f7ea26e 100644
--- a/projects/common/src/examples/java/com/example/examplemod/peripheral/FurnacePeripheral.java
+++ b/projects/common/src/examples/java/com/example/examplemod/peripheral/FurnacePeripheral.java
@@ -23,7 +23,7 @@ public class FurnacePeripheral implements GenericPeripheral {
@LuaFunction(mainThread = true)
public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
// Don't do it this way! Use an access widener/transformer to access the "litTime" field instead.
- return furnace.saveWithoutMetadata(furnace.getLevel().registryAccess()).getInt("BurnTime");
+ return furnace.saveWithoutMetadata(furnace.getLevel().registryAccess()).getShortOr("lit_time_remaining", (short) 0);
}
}
// @end region=body
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/cable.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/cable.json
index a440c3e2c..2c00e3b80 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/cable.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/cable.json
@@ -10,7 +10,7 @@
}
},
{
- "apply": {"model": "computercraft:block/cable_core_facing", "y": 0},
+ "apply": {"model": "computercraft:block/cable_core_facing"},
"when": {
"OR": [
{
@@ -55,70 +55,70 @@
]
}
},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 270, "y": 0}, "when": {"down": "true"}},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 90, "y": 0}, "when": {"up": "true"}},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 0, "y": 180}, "when": {"north": "true"}},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 0, "y": 0}, "when": {"south": "true"}},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 0, "y": 90}, "when": {"west": "true"}},
- {"apply": {"model": "computercraft:block/cable_arm", "x": 0, "y": 270}, "when": {"east": "true"}},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 90, "y": 0}, "when": {"modem": "down_off"}},
+ {"apply": {"model": "computercraft:block/cable_arm", "x": 270}, "when": {"down": "true"}},
+ {"apply": {"model": "computercraft:block/cable_arm", "x": 90}, "when": {"up": "true"}},
+ {"apply": {"model": "computercraft:block/cable_arm", "y": 180}, "when": {"north": "true"}},
+ {"apply": {"model": "computercraft:block/cable_arm"}, "when": {"south": "true"}},
+ {"apply": {"model": "computercraft:block/cable_arm", "y": 90}, "when": {"west": "true"}},
+ {"apply": {"model": "computercraft:block/cable_arm", "y": 270}, "when": {"east": "true"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off", "x": 270}, "when": {"modem": "down_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 90, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 270},
"when": {"modem": "down_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 90, "y": 0}, "when": {"modem": "down_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on", "x": 270}, "when": {"modem": "down_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 90, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 270},
"when": {"modem": "down_on_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 270, "y": 0}, "when": {"modem": "up_off"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off", "x": 90}, "when": {"modem": "up_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 270, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 90},
"when": {"modem": "up_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 270, "y": 0}, "when": {"modem": "up_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on", "x": 90}, "when": {"modem": "up_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 270, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 90},
"when": {"modem": "up_on_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 0, "y": 0}, "when": {"modem": "north_off"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off", "y": 180}, "when": {"modem": "north_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 0, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "y": 180},
"when": {"modem": "north_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 0, "y": 0}, "when": {"modem": "north_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on", "y": 180}, "when": {"modem": "north_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 0, "y": 0},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "y": 180},
"when": {"modem": "north_on_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 0, "y": 180}, "when": {"modem": "south_off"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off"}, "when": {"modem": "south_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 0, "y": 180},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral"},
"when": {"modem": "south_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 0, "y": 180}, "when": {"modem": "south_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on"}, "when": {"modem": "south_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 0, "y": 180},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral"},
"when": {"modem": "south_on_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 0, "y": 270}, "when": {"modem": "west_off"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off", "y": 90}, "when": {"modem": "west_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 0, "y": 270},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "y": 90},
"when": {"modem": "west_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 0, "y": 270}, "when": {"modem": "west_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on", "y": 90}, "when": {"modem": "west_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 0, "y": 270},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "y": 90},
"when": {"modem": "west_on_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_off", "x": 0, "y": 90}, "when": {"modem": "east_off"}},
+ {"apply": {"model": "computercraft:block/wired_modem_off", "y": 270}, "when": {"modem": "east_off"}},
{
- "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "x": 0, "y": 90},
+ "apply": {"model": "computercraft:block/wired_modem_off_peripheral", "y": 270},
"when": {"modem": "east_off_peripheral"}
},
- {"apply": {"model": "computercraft:block/wired_modem_on", "x": 0, "y": 90}, "when": {"modem": "east_on"}},
+ {"apply": {"model": "computercraft:block/wired_modem_on", "y": 270}, "when": {"modem": "east_on"}},
{
- "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "x": 0, "y": 90},
+ "apply": {"model": "computercraft:block/wired_modem_on_peripheral", "y": 270},
"when": {"modem": "east_on_peripheral"}
}
]
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json
index 5f5960311..a13c25e88 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json
@@ -3,9 +3,9 @@
"facing=east,state=blinking": {"model": "computercraft:block/computer_advanced_blinking", "y": 90},
"facing=east,state=off": {"model": "computercraft:block/computer_advanced_off", "y": 90},
"facing=east,state=on": {"model": "computercraft:block/computer_advanced_on", "y": 90},
- "facing=north,state=blinking": {"model": "computercraft:block/computer_advanced_blinking", "y": 0},
- "facing=north,state=off": {"model": "computercraft:block/computer_advanced_off", "y": 0},
- "facing=north,state=on": {"model": "computercraft:block/computer_advanced_on", "y": 0},
+ "facing=north,state=blinking": {"model": "computercraft:block/computer_advanced_blinking"},
+ "facing=north,state=off": {"model": "computercraft:block/computer_advanced_off"},
+ "facing=north,state=on": {"model": "computercraft:block/computer_advanced_on"},
"facing=south,state=blinking": {"model": "computercraft:block/computer_advanced_blinking", "y": 180},
"facing=south,state=off": {"model": "computercraft:block/computer_advanced_off", "y": 180},
"facing=south,state=on": {"model": "computercraft:block/computer_advanced_on", "y": 180},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_command.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_command.json
index 2077c3fbf..07560bc5f 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_command.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_command.json
@@ -3,9 +3,9 @@
"facing=east,state=blinking": {"model": "computercraft:block/computer_command_blinking", "y": 90},
"facing=east,state=off": {"model": "computercraft:block/computer_command_off", "y": 90},
"facing=east,state=on": {"model": "computercraft:block/computer_command_on", "y": 90},
- "facing=north,state=blinking": {"model": "computercraft:block/computer_command_blinking", "y": 0},
- "facing=north,state=off": {"model": "computercraft:block/computer_command_off", "y": 0},
- "facing=north,state=on": {"model": "computercraft:block/computer_command_on", "y": 0},
+ "facing=north,state=blinking": {"model": "computercraft:block/computer_command_blinking"},
+ "facing=north,state=off": {"model": "computercraft:block/computer_command_off"},
+ "facing=north,state=on": {"model": "computercraft:block/computer_command_on"},
"facing=south,state=blinking": {"model": "computercraft:block/computer_command_blinking", "y": 180},
"facing=south,state=off": {"model": "computercraft:block/computer_command_off", "y": 180},
"facing=south,state=on": {"model": "computercraft:block/computer_command_on", "y": 180},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_normal.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_normal.json
index 534584f1d..25ffc3938 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_normal.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/computer_normal.json
@@ -3,9 +3,9 @@
"facing=east,state=blinking": {"model": "computercraft:block/computer_normal_blinking", "y": 90},
"facing=east,state=off": {"model": "computercraft:block/computer_normal_off", "y": 90},
"facing=east,state=on": {"model": "computercraft:block/computer_normal_on", "y": 90},
- "facing=north,state=blinking": {"model": "computercraft:block/computer_normal_blinking", "y": 0},
- "facing=north,state=off": {"model": "computercraft:block/computer_normal_off", "y": 0},
- "facing=north,state=on": {"model": "computercraft:block/computer_normal_on", "y": 0},
+ "facing=north,state=blinking": {"model": "computercraft:block/computer_normal_blinking"},
+ "facing=north,state=off": {"model": "computercraft:block/computer_normal_off"},
+ "facing=north,state=on": {"model": "computercraft:block/computer_normal_on"},
"facing=south,state=blinking": {"model": "computercraft:block/computer_normal_blinking", "y": 180},
"facing=south,state=off": {"model": "computercraft:block/computer_normal_off", "y": 180},
"facing=south,state=on": {"model": "computercraft:block/computer_normal_on", "y": 180},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/disk_drive.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/disk_drive.json
index ea03105fb..e1bb92d2b 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/disk_drive.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/disk_drive.json
@@ -3,9 +3,9 @@
"facing=east,state=empty": {"model": "computercraft:block/disk_drive_empty", "y": 90},
"facing=east,state=full": {"model": "computercraft:block/disk_drive_full", "y": 90},
"facing=east,state=invalid": {"model": "computercraft:block/disk_drive_invalid", "y": 90},
- "facing=north,state=empty": {"model": "computercraft:block/disk_drive_empty", "y": 0},
- "facing=north,state=full": {"model": "computercraft:block/disk_drive_full", "y": 0},
- "facing=north,state=invalid": {"model": "computercraft:block/disk_drive_invalid", "y": 0},
+ "facing=north,state=empty": {"model": "computercraft:block/disk_drive_empty"},
+ "facing=north,state=full": {"model": "computercraft:block/disk_drive_full"},
+ "facing=north,state=invalid": {"model": "computercraft:block/disk_drive_invalid"},
"facing=south,state=empty": {"model": "computercraft:block/disk_drive_empty", "y": 180},
"facing=south,state=full": {"model": "computercraft:block/disk_drive_full", "y": 180},
"facing=south,state=invalid": {"model": "computercraft:block/disk_drive_invalid", "y": 180},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
index c760753f3..a5ec50c9d 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/lectern.json
@@ -1,7 +1,7 @@
{
"variants": {
"facing=east": {"model": "minecraft:block/lectern", "y": 90},
- "facing=north": {"model": "minecraft:block/lectern", "y": 0},
+ "facing=north": {"model": "minecraft:block/lectern"},
"facing=south": {"model": "minecraft:block/lectern", "y": 180},
"facing=west": {"model": "minecraft:block/lectern", "y": 270}
}
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json
index 7f85fcc59..ceebe2fb3 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json
@@ -20,26 +20,22 @@
"facing=east,orientation=down,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 90, "y": 90},
"facing=east,orientation=down,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 90, "y": 90},
"facing=east,orientation=down,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 90, "y": 90},
- "facing=east,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 0, "y": 90},
- "facing=east,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud",
- "x": 0,
- "y": 90
- },
- "facing=east,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 0, "y": 90},
- "facing=east,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "x": 0, "y": 90},
- "facing=east,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 0, "y": 90},
- "facing=east,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 0, "y": 90},
- "facing=east,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 0, "y": 90},
- "facing=east,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 0, "y": 90},
+ "facing=east,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "y": 90},
+ "facing=east,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "y": 90},
+ "facing=east,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "y": 90},
+ "facing=east,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "y": 90},
+ "facing=east,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "y": 90},
+ "facing=east,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "y": 90},
+ "facing=east,orientation=north,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "y": 90},
+ "facing=east,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "y": 90},
+ "facing=east,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "y": 90},
+ "facing=east,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "y": 90},
+ "facing=east,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "y": 90},
+ "facing=east,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "y": 90},
+ "facing=east,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "y": 90},
+ "facing=east,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "y": 90},
+ "facing=east,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "y": 90},
+ "facing=east,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "y": 90},
"facing=east,orientation=up,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 270, "y": 90},
"facing=east,orientation=up,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 270, "y": 90},
"facing=east,orientation=up,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 270, "y": 90},
@@ -56,62 +52,54 @@
"facing=east,orientation=up,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 270, "y": 90},
"facing=east,orientation=up,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 270, "y": 90},
"facing=east,orientation=up,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 270, "y": 90},
- "facing=north,orientation=down,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 90, "y": 0},
- "facing=north,orientation=down,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud",
- "x": 90,
- "y": 0
- },
- "facing=north,orientation=down,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 90, "y": 0},
- "facing=north,orientation=down,state=none": {"model": "computercraft:block/monitor_advanced", "x": 90, "y": 0},
- "facing=north,orientation=down,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 90, "y": 0},
- "facing=north,orientation=down,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 90, "y": 0},
- "facing=north,orientation=down,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 90, "y": 0},
- "facing=north,orientation=down,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 90, "y": 0},
- "facing=north,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 0, "y": 0},
- "facing=north,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud",
- "x": 0,
- "y": 0
- },
- "facing=north,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 0, "y": 0},
- "facing=north,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "x": 0, "y": 0},
- "facing=north,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 0, "y": 0},
- "facing=north,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 0, "y": 0},
- "facing=north,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 0, "y": 0},
- "facing=north,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 0, "y": 0},
- "facing=north,orientation=up,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 270, "y": 0},
- "facing=north,orientation=up,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=none": {"model": "computercraft:block/monitor_advanced", "x": 270, "y": 0},
- "facing=north,orientation=up,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 270, "y": 0},
- "facing=north,orientation=up,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 270, "y": 0},
- "facing=north,orientation=up,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 270, "y": 0},
+ "facing=north,orientation=down,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 90},
+ "facing=north,orientation=down,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 90},
+ "facing=north,orientation=down,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 90},
+ "facing=north,orientation=down,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 90},
+ "facing=north,orientation=down,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 90},
+ "facing=north,orientation=down,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 90},
+ "facing=north,orientation=down,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "x": 90},
+ "facing=north,orientation=down,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 90},
+ "facing=north,orientation=down,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 90},
+ "facing=north,orientation=down,state=none": {"model": "computercraft:block/monitor_advanced", "x": 90},
+ "facing=north,orientation=down,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 90},
+ "facing=north,orientation=down,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 90},
+ "facing=north,orientation=down,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 90},
+ "facing=north,orientation=down,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 90},
+ "facing=north,orientation=down,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 90},
+ "facing=north,orientation=down,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 90},
+ "facing=north,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d"},
+ "facing=north,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l"},
+ "facing=north,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld"},
+ "facing=north,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr"},
+ "facing=north,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd"},
+ "facing=north,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru"},
+ "facing=north,orientation=north,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud"},
+ "facing=north,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu"},
+ "facing=north,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud"},
+ "facing=north,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced"},
+ "facing=north,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r"},
+ "facing=north,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd"},
+ "facing=north,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru"},
+ "facing=north,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud"},
+ "facing=north,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u"},
+ "facing=north,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud"},
+ "facing=north,orientation=up,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 270},
+ "facing=north,orientation=up,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 270},
+ "facing=north,orientation=up,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 270},
+ "facing=north,orientation=up,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 270},
+ "facing=north,orientation=up,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 270},
+ "facing=north,orientation=up,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 270},
+ "facing=north,orientation=up,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "x": 270},
+ "facing=north,orientation=up,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 270},
+ "facing=north,orientation=up,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 270},
+ "facing=north,orientation=up,state=none": {"model": "computercraft:block/monitor_advanced", "x": 270},
+ "facing=north,orientation=up,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 270},
+ "facing=north,orientation=up,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 270},
+ "facing=north,orientation=up,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 270},
+ "facing=north,orientation=up,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 270},
+ "facing=north,orientation=up,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 270},
+ "facing=north,orientation=up,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 270},
"facing=south,orientation=down,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 90, "y": 180},
"facing=south,orientation=down,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 90, "y": 180},
"facing=south,orientation=down,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 90, "y": 180},
@@ -148,42 +136,22 @@
},
"facing=south,orientation=down,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 90, "y": 180},
"facing=south,orientation=down,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 90, "y": 180},
- "facing=south,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 0, "y": 180},
- "facing=south,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "x": 0, "y": 180},
- "facing=south,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 0, "y": 180},
- "facing=south,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 0, "y": 180},
- "facing=south,orientation=north,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 0, "y": 180},
+ "facing=south,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "y": 180},
+ "facing=south,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "y": 180},
+ "facing=south,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "y": 180},
+ "facing=south,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "y": 180},
+ "facing=south,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "y": 180},
+ "facing=south,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "y": 180},
+ "facing=south,orientation=north,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "y": 180},
+ "facing=south,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "y": 180},
+ "facing=south,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "y": 180},
+ "facing=south,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "y": 180},
+ "facing=south,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "y": 180},
+ "facing=south,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "y": 180},
+ "facing=south,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "y": 180},
+ "facing=south,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "y": 180},
+ "facing=south,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "y": 180},
+ "facing=south,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "y": 180},
"facing=south,orientation=up,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 270, "y": 180},
"facing=south,orientation=up,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 270, "y": 180},
"facing=south,orientation=up,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 270, "y": 180},
@@ -224,26 +192,22 @@
"facing=west,orientation=down,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 90, "y": 270},
"facing=west,orientation=down,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 90, "y": 270},
"facing=west,orientation=down,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 90, "y": 270},
- "facing=west,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 0, "y": 270},
- "facing=west,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud",
- "x": 0,
- "y": 270
- },
- "facing=west,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "x": 0, "y": 270},
- "facing=west,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "x": 0, "y": 270},
- "facing=west,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "x": 0, "y": 270},
- "facing=west,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "x": 0, "y": 270},
- "facing=west,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "x": 0, "y": 270},
- "facing=west,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "x": 0, "y": 270},
+ "facing=west,orientation=north,state=d": {"model": "computercraft:block/monitor_advanced_d", "y": 270},
+ "facing=west,orientation=north,state=l": {"model": "computercraft:block/monitor_advanced_l", "y": 270},
+ "facing=west,orientation=north,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "y": 270},
+ "facing=west,orientation=north,state=lr": {"model": "computercraft:block/monitor_advanced_lr", "y": 270},
+ "facing=west,orientation=north,state=lrd": {"model": "computercraft:block/monitor_advanced_lrd", "y": 270},
+ "facing=west,orientation=north,state=lru": {"model": "computercraft:block/monitor_advanced_lru", "y": 270},
+ "facing=west,orientation=north,state=lrud": {"model": "computercraft:block/monitor_advanced_lrud", "y": 270},
+ "facing=west,orientation=north,state=lu": {"model": "computercraft:block/monitor_advanced_lu", "y": 270},
+ "facing=west,orientation=north,state=lud": {"model": "computercraft:block/monitor_advanced_lud", "y": 270},
+ "facing=west,orientation=north,state=none": {"model": "computercraft:block/monitor_advanced", "y": 270},
+ "facing=west,orientation=north,state=r": {"model": "computercraft:block/monitor_advanced_r", "y": 270},
+ "facing=west,orientation=north,state=rd": {"model": "computercraft:block/monitor_advanced_rd", "y": 270},
+ "facing=west,orientation=north,state=ru": {"model": "computercraft:block/monitor_advanced_ru", "y": 270},
+ "facing=west,orientation=north,state=rud": {"model": "computercraft:block/monitor_advanced_rud", "y": 270},
+ "facing=west,orientation=north,state=u": {"model": "computercraft:block/monitor_advanced_u", "y": 270},
+ "facing=west,orientation=north,state=ud": {"model": "computercraft:block/monitor_advanced_ud", "y": 270},
"facing=west,orientation=up,state=d": {"model": "computercraft:block/monitor_advanced_d", "x": 270, "y": 270},
"facing=west,orientation=up,state=l": {"model": "computercraft:block/monitor_advanced_l", "x": 270, "y": 270},
"facing=west,orientation=up,state=ld": {"model": "computercraft:block/monitor_advanced_ld", "x": 270, "y": 270},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json
index 6c966368b..931774193 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json
@@ -16,22 +16,22 @@
"facing=east,orientation=down,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 90, "y": 90},
"facing=east,orientation=down,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 90, "y": 90},
"facing=east,orientation=down,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 90, "y": 90},
- "facing=east,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 0, "y": 90},
- "facing=east,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 0, "y": 90},
- "facing=east,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 0, "y": 90},
- "facing=east,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "x": 0, "y": 90},
- "facing=east,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 0, "y": 90},
- "facing=east,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 0, "y": 90},
- "facing=east,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 0, "y": 90},
- "facing=east,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 0, "y": 90},
- "facing=east,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 0, "y": 90},
+ "facing=east,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "y": 90},
+ "facing=east,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "y": 90},
+ "facing=east,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "y": 90},
+ "facing=east,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "y": 90},
+ "facing=east,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "y": 90},
+ "facing=east,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "y": 90},
+ "facing=east,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "y": 90},
+ "facing=east,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "y": 90},
+ "facing=east,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "y": 90},
+ "facing=east,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "y": 90},
+ "facing=east,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "y": 90},
+ "facing=east,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "y": 90},
+ "facing=east,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "y": 90},
+ "facing=east,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "y": 90},
+ "facing=east,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "y": 90},
+ "facing=east,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "y": 90},
"facing=east,orientation=up,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 270, "y": 90},
"facing=east,orientation=up,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 270, "y": 90},
"facing=east,orientation=up,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 270, "y": 90},
@@ -48,54 +48,54 @@
"facing=east,orientation=up,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 270, "y": 90},
"facing=east,orientation=up,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 270, "y": 90},
"facing=east,orientation=up,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 270, "y": 90},
- "facing=north,orientation=down,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 90, "y": 0},
- "facing=north,orientation=down,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 90, "y": 0},
- "facing=north,orientation=down,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 90, "y": 0},
- "facing=north,orientation=down,state=none": {"model": "computercraft:block/monitor_normal", "x": 90, "y": 0},
- "facing=north,orientation=down,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 90, "y": 0},
- "facing=north,orientation=down,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 90, "y": 0},
- "facing=north,orientation=down,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 90, "y": 0},
- "facing=north,orientation=down,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 90, "y": 0},
- "facing=north,orientation=down,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 90, "y": 0},
- "facing=north,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 0, "y": 0},
- "facing=north,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 0, "y": 0},
- "facing=north,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 0, "y": 0},
- "facing=north,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "x": 0, "y": 0},
- "facing=north,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 0, "y": 0},
- "facing=north,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 0, "y": 0},
- "facing=north,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 0, "y": 0},
- "facing=north,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 0, "y": 0},
- "facing=north,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 0, "y": 0},
- "facing=north,orientation=up,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 270, "y": 0},
- "facing=north,orientation=up,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 270, "y": 0},
- "facing=north,orientation=up,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=none": {"model": "computercraft:block/monitor_normal", "x": 270, "y": 0},
- "facing=north,orientation=up,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 270, "y": 0},
- "facing=north,orientation=up,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 270, "y": 0},
- "facing=north,orientation=up,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 270, "y": 0},
- "facing=north,orientation=up,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 270, "y": 0},
- "facing=north,orientation=up,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 270, "y": 0},
+ "facing=north,orientation=down,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 90},
+ "facing=north,orientation=down,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 90},
+ "facing=north,orientation=down,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 90},
+ "facing=north,orientation=down,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 90},
+ "facing=north,orientation=down,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 90},
+ "facing=north,orientation=down,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 90},
+ "facing=north,orientation=down,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 90},
+ "facing=north,orientation=down,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 90},
+ "facing=north,orientation=down,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 90},
+ "facing=north,orientation=down,state=none": {"model": "computercraft:block/monitor_normal", "x": 90},
+ "facing=north,orientation=down,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 90},
+ "facing=north,orientation=down,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 90},
+ "facing=north,orientation=down,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 90},
+ "facing=north,orientation=down,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 90},
+ "facing=north,orientation=down,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 90},
+ "facing=north,orientation=down,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 90},
+ "facing=north,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d"},
+ "facing=north,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l"},
+ "facing=north,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld"},
+ "facing=north,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr"},
+ "facing=north,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd"},
+ "facing=north,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru"},
+ "facing=north,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud"},
+ "facing=north,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu"},
+ "facing=north,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud"},
+ "facing=north,orientation=north,state=none": {"model": "computercraft:block/monitor_normal"},
+ "facing=north,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r"},
+ "facing=north,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd"},
+ "facing=north,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru"},
+ "facing=north,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud"},
+ "facing=north,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u"},
+ "facing=north,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud"},
+ "facing=north,orientation=up,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 270},
+ "facing=north,orientation=up,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 270},
+ "facing=north,orientation=up,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 270},
+ "facing=north,orientation=up,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 270},
+ "facing=north,orientation=up,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 270},
+ "facing=north,orientation=up,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 270},
+ "facing=north,orientation=up,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 270},
+ "facing=north,orientation=up,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 270},
+ "facing=north,orientation=up,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 270},
+ "facing=north,orientation=up,state=none": {"model": "computercraft:block/monitor_normal", "x": 270},
+ "facing=north,orientation=up,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 270},
+ "facing=north,orientation=up,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 270},
+ "facing=north,orientation=up,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 270},
+ "facing=north,orientation=up,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 270},
+ "facing=north,orientation=up,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 270},
+ "facing=north,orientation=up,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 270},
"facing=south,orientation=down,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 90, "y": 180},
"facing=south,orientation=down,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 90, "y": 180},
"facing=south,orientation=down,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 90, "y": 180},
@@ -116,26 +116,22 @@
"facing=south,orientation=down,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 90, "y": 180},
"facing=south,orientation=down,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 90, "y": 180},
"facing=south,orientation=down,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 90, "y": 180},
- "facing=south,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 0, "y": 180},
- "facing=south,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud",
- "x": 0,
- "y": 180
- },
- "facing=south,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 0, "y": 180},
- "facing=south,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 0, "y": 180},
- "facing=south,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "x": 0, "y": 180},
- "facing=south,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 0, "y": 180},
- "facing=south,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 0, "y": 180},
- "facing=south,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 0, "y": 180},
- "facing=south,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 0, "y": 180},
- "facing=south,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 0, "y": 180},
+ "facing=south,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "y": 180},
+ "facing=south,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "y": 180},
+ "facing=south,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "y": 180},
+ "facing=south,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "y": 180},
+ "facing=south,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "y": 180},
+ "facing=south,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "y": 180},
+ "facing=south,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "y": 180},
+ "facing=south,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "y": 180},
+ "facing=south,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "y": 180},
+ "facing=south,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "y": 180},
+ "facing=south,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "y": 180},
+ "facing=south,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "y": 180},
+ "facing=south,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "y": 180},
+ "facing=south,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "y": 180},
+ "facing=south,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "y": 180},
+ "facing=south,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "y": 180},
"facing=south,orientation=up,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 270, "y": 180},
"facing=south,orientation=up,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 270, "y": 180},
"facing=south,orientation=up,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 270, "y": 180},
@@ -168,22 +164,22 @@
"facing=west,orientation=down,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 90, "y": 270},
"facing=west,orientation=down,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 90, "y": 270},
"facing=west,orientation=down,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 90, "y": 270},
- "facing=west,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 0, "y": 270},
- "facing=west,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "x": 0, "y": 270},
- "facing=west,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "x": 0, "y": 270},
- "facing=west,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "x": 0, "y": 270},
- "facing=west,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "x": 0, "y": 270},
- "facing=west,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "x": 0, "y": 270},
- "facing=west,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "x": 0, "y": 270},
- "facing=west,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "x": 0, "y": 270},
- "facing=west,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "x": 0, "y": 270},
+ "facing=west,orientation=north,state=d": {"model": "computercraft:block/monitor_normal_d", "y": 270},
+ "facing=west,orientation=north,state=l": {"model": "computercraft:block/monitor_normal_l", "y": 270},
+ "facing=west,orientation=north,state=ld": {"model": "computercraft:block/monitor_normal_ld", "y": 270},
+ "facing=west,orientation=north,state=lr": {"model": "computercraft:block/monitor_normal_lr", "y": 270},
+ "facing=west,orientation=north,state=lrd": {"model": "computercraft:block/monitor_normal_lrd", "y": 270},
+ "facing=west,orientation=north,state=lru": {"model": "computercraft:block/monitor_normal_lru", "y": 270},
+ "facing=west,orientation=north,state=lrud": {"model": "computercraft:block/monitor_normal_lrud", "y": 270},
+ "facing=west,orientation=north,state=lu": {"model": "computercraft:block/monitor_normal_lu", "y": 270},
+ "facing=west,orientation=north,state=lud": {"model": "computercraft:block/monitor_normal_lud", "y": 270},
+ "facing=west,orientation=north,state=none": {"model": "computercraft:block/monitor_normal", "y": 270},
+ "facing=west,orientation=north,state=r": {"model": "computercraft:block/monitor_normal_r", "y": 270},
+ "facing=west,orientation=north,state=rd": {"model": "computercraft:block/monitor_normal_rd", "y": 270},
+ "facing=west,orientation=north,state=ru": {"model": "computercraft:block/monitor_normal_ru", "y": 270},
+ "facing=west,orientation=north,state=rud": {"model": "computercraft:block/monitor_normal_rud", "y": 270},
+ "facing=west,orientation=north,state=u": {"model": "computercraft:block/monitor_normal_u", "y": 270},
+ "facing=west,orientation=north,state=ud": {"model": "computercraft:block/monitor_normal_ud", "y": 270},
"facing=west,orientation=up,state=d": {"model": "computercraft:block/monitor_normal_d", "x": 270, "y": 270},
"facing=west,orientation=up,state=l": {"model": "computercraft:block/monitor_normal_l", "x": 270, "y": 270},
"facing=west,orientation=up,state=ld": {"model": "computercraft:block/monitor_normal_ld", "x": 270, "y": 270},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/printer.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/printer.json
index 2166b5f04..027934678 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/printer.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/printer.json
@@ -2,16 +2,16 @@
"variants": {
"bottom=false,facing=east,top=false": {"model": "computercraft:block/printer_empty", "y": 90},
"bottom=false,facing=east,top=true": {"model": "computercraft:block/printer_top_full", "y": 90},
- "bottom=false,facing=north,top=false": {"model": "computercraft:block/printer_empty", "y": 0},
- "bottom=false,facing=north,top=true": {"model": "computercraft:block/printer_top_full", "y": 0},
+ "bottom=false,facing=north,top=false": {"model": "computercraft:block/printer_empty"},
+ "bottom=false,facing=north,top=true": {"model": "computercraft:block/printer_top_full"},
"bottom=false,facing=south,top=false": {"model": "computercraft:block/printer_empty", "y": 180},
"bottom=false,facing=south,top=true": {"model": "computercraft:block/printer_top_full", "y": 180},
"bottom=false,facing=west,top=false": {"model": "computercraft:block/printer_empty", "y": 270},
"bottom=false,facing=west,top=true": {"model": "computercraft:block/printer_top_full", "y": 270},
"bottom=true,facing=east,top=false": {"model": "computercraft:block/printer_bottom_full", "y": 90},
"bottom=true,facing=east,top=true": {"model": "computercraft:block/printer_both_full", "y": 90},
- "bottom=true,facing=north,top=false": {"model": "computercraft:block/printer_bottom_full", "y": 0},
- "bottom=true,facing=north,top=true": {"model": "computercraft:block/printer_both_full", "y": 0},
+ "bottom=true,facing=north,top=false": {"model": "computercraft:block/printer_bottom_full"},
+ "bottom=true,facing=north,top=true": {"model": "computercraft:block/printer_both_full"},
"bottom=true,facing=south,top=false": {"model": "computercraft:block/printer_bottom_full", "y": 180},
"bottom=true,facing=south,top=true": {"model": "computercraft:block/printer_both_full", "y": 180},
"bottom=true,facing=west,top=false": {"model": "computercraft:block/printer_bottom_full", "y": 270},
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
index 9f029e6da..e70054318 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
@@ -1,16 +1,16 @@
{
"variants": {
- "facing=down,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 90, "y": 0},
- "facing=down,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 90, "y": 0},
- "facing=east,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 0, "y": 90},
- "facing=east,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 0, "y": 90},
- "facing=north,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 0, "y": 0},
- "facing=north,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 0, "y": 0},
- "facing=south,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 0, "y": 180},
- "facing=south,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 0, "y": 180},
- "facing=up,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 270, "y": 0},
- "facing=up,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 270, "y": 0},
- "facing=west,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 0, "y": 270},
- "facing=west,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 0, "y": 270}
+ "facing=down,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 90},
+ "facing=down,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 90},
+ "facing=east,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "y": 90},
+ "facing=east,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "y": 90},
+ "facing=north,on=false": {"model": "computercraft:block/wireless_modem_advanced_off"},
+ "facing=north,on=true": {"model": "computercraft:block/wireless_modem_advanced_on"},
+ "facing=south,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "y": 180},
+ "facing=south,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "y": 180},
+ "facing=up,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "x": 270},
+ "facing=up,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "x": 270},
+ "facing=west,on=false": {"model": "computercraft:block/wireless_modem_advanced_off", "y": 270},
+ "facing=west,on=true": {"model": "computercraft:block/wireless_modem_advanced_on", "y": 270}
}
}
diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json
index 09623ddbf..6faadf248 100644
--- a/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json
+++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json
@@ -1,16 +1,16 @@
{
"variants": {
- "facing=down,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 90, "y": 0},
- "facing=down,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 90, "y": 0},
- "facing=east,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 0, "y": 90},
- "facing=east,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 0, "y": 90},
- "facing=north,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 0, "y": 0},
- "facing=north,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 0, "y": 0},
- "facing=south,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 0, "y": 180},
- "facing=south,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 0, "y": 180},
- "facing=up,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 270, "y": 0},
- "facing=up,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 270, "y": 0},
- "facing=west,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 0, "y": 270},
- "facing=west,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 0, "y": 270}
+ "facing=down,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 90},
+ "facing=down,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 90},
+ "facing=east,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "y": 90},
+ "facing=east,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "y": 90},
+ "facing=north,on=false": {"model": "computercraft:block/wireless_modem_normal_off"},
+ "facing=north,on=true": {"model": "computercraft:block/wireless_modem_normal_on"},
+ "facing=south,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "y": 180},
+ "facing=south,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "y": 180},
+ "facing=up,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "x": 270},
+ "facing=up,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "x": 270},
+ "facing=west,on=false": {"model": "computercraft:block/wireless_modem_normal_off", "y": 270},
+ "facing=west,on=true": {"model": "computercraft:block/wireless_modem_normal_on", "y": 270}
}
}
diff --git a/projects/common/src/main/java/dan200/computercraft/mixin/V1460Mixin.java b/projects/common/src/main/java/dan200/computercraft/mixin/V1460Mixin.java
index 5a5f57af9..427d93cd2 100644
--- a/projects/common/src/main/java/dan200/computercraft/mixin/V1460Mixin.java
+++ b/projects/common/src/main/java/dan200/computercraft/mixin/V1460Mixin.java
@@ -44,7 +44,8 @@ class V1460Mixin {
// Disk drives contain a single item
schema.register(map, "computercraft:disk_drive", () -> DSL.optionalFields(
- "Item", References.ITEM_STACK.in(schema)
+ "Item", References.ITEM_STACK.in(schema),
+ "CustomName", References.TEXT_COMPONENT.in(schema)
));
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java
index 2acdf06ee..d48989ab3 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java
@@ -13,7 +13,6 @@ import dan200.computercraft.shared.lectern.CustomLecternBlock;
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.TickScheduler;
-import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
@@ -29,7 +28,7 @@ import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.*;
-import net.minecraft.world.item.component.TooltipProvider;
+import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LecternBlock;
@@ -170,18 +169,12 @@ public final class CommonHooks {
public static void onItemTooltip(ItemStack stack, Item.TooltipContext context, TooltipFlag flags, List out) {
var appender = new TooltipAppender(out);
- addToTooltip(stack, ModRegistry.DataComponents.PRINTOUT.get(), context, appender, flags);
- addToTooltip(stack, ModRegistry.DataComponents.TREASURE_DISK.get(), context, appender, flags);
- // Disk and computer IDs require some conditional logic, so we don't bother using TooltipProvider.
-
- var diskId = stack.get(ModRegistry.DataComponents.DISK_ID.get());
- if (diskId != null && flags.isAdvanced()) diskId.addToTooltip("gui.computercraft.tooltip.disk_id", appender);
-
- var computerId = stack.get(ModRegistry.DataComponents.COMPUTER_ID.get());
- if (computerId != null && (flags.isAdvanced() || !stack.has(DataComponents.CUSTOM_NAME))) {
- computerId.addToTooltip("gui.computercraft.tooltip.computer_id", appender);
- }
+ var display = stack.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT);
+ stack.addToTooltip(ModRegistry.DataComponents.PRINTOUT.get(), context, display, appender, flags);
+ stack.addToTooltip(ModRegistry.DataComponents.TREASURE_DISK.get(), context, display, appender, flags);
+ stack.addToTooltip(ModRegistry.DataComponents.COMPUTER_ID.get(), context, display, appender, flags);
+ stack.addToTooltip(ModRegistry.DataComponents.DISK_ID.get(), context, display, appender, flags);
}
/**
@@ -200,9 +193,4 @@ public final class CommonHooks {
out.add(index++, component);
}
}
-
- private static void addToTooltip(ItemStack stack, DataComponentType component, Item.TooltipContext context, Consumer out, TooltipFlag flags) {
- var provider = stack.get(component);
- if (provider != null) provider.addToTooltip(context, out, flags);
- }
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java
index 9b21d0270..db010f211 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java
@@ -19,7 +19,6 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.api.upgrades.UpgradeType;
-import dan200.computercraft.core.util.Colour;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.shared.command.UserLevel;
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
@@ -113,7 +112,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.*;
-import net.minecraft.world.item.component.DyedItemColor;
+import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
@@ -274,6 +273,13 @@ public final class ModRegistry {
return new Item.Properties();
}
+ private static Item.Properties dyeableProperties() {
+ return properties().component(
+ net.minecraft.core.component.DataComponents.TOOLTIP_DISPLAY,
+ TooltipDisplay.DEFAULT.withHidden(net.minecraft.core.component.DataComponents.DYED_COLOR, true)
+ );
+ }
+
private static RegistryEntry register(String name, Function build, Supplier properties) {
return REGISTRY.register(name, () -> build.apply(properties.get().setId(ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, name)))));
}
@@ -282,26 +288,26 @@ public final class ModRegistry {
return register(name, build, () -> properties);
}
- private static RegistryEntry ofBlock(RegistryEntry parent, BiFunction supplier) {
- return register(parent.id().getPath(), p -> supplier.apply(parent.get(), p), properties().useBlockDescriptionPrefix());
+ private static RegistryEntry ofBlock(RegistryEntry parent, BiFunction supplier, Item.Properties properties) {
+ return register(parent.id().getPath(), p -> supplier.apply(parent.get(), p), properties.useBlockDescriptionPrefix());
}
- public static final RegistryEntry COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, BlockItem::new);
- public static final RegistryEntry COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, BlockItem::new);
- public static final RegistryEntry COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, GameMasterBlockItem::new);
+ public static final RegistryEntry COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, BlockItem::new, properties());
+ public static final RegistryEntry COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, BlockItem::new, properties());
+ public static final RegistryEntry COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, GameMasterBlockItem::new, properties());
public static final RegistryEntry POCKET_COMPUTER_NORMAL = register("pocket_computer_normal",
- p -> new PocketComputerItem(p, ComputerFamily.NORMAL), properties().stacksTo(1));
+ p -> new PocketComputerItem(p, ComputerFamily.NORMAL), dyeableProperties());
public static final RegistryEntry POCKET_COMPUTER_ADVANCED = register("pocket_computer_advanced",
- p -> new PocketComputerItem(p, ComputerFamily.ADVANCED), properties().stacksTo(1));
+ p -> new PocketComputerItem(p, ComputerFamily.ADVANCED), dyeableProperties());
- public static final RegistryEntry TURTLE_NORMAL = ofBlock(Blocks.TURTLE_NORMAL, TurtleItem::new);
- public static final RegistryEntry TURTLE_ADVANCED = ofBlock(Blocks.TURTLE_ADVANCED, TurtleItem::new);
+ public static final RegistryEntry TURTLE_NORMAL = ofBlock(Blocks.TURTLE_NORMAL, TurtleItem::new, dyeableProperties());
+ public static final RegistryEntry TURTLE_ADVANCED = ofBlock(Blocks.TURTLE_ADVANCED, TurtleItem::new, dyeableProperties());
public static final RegistryEntry DISK =
- register("disk", DiskItem::new, properties().stacksTo(1));
+ register("disk", DiskItem::new, dyeableProperties().stacksTo(1));
public static final RegistryEntry TREASURE_DISK =
- register("treasure_disk", DiskItem::new, properties().stacksTo(1));
+ register("treasure_disk", DiskItem::new, dyeableProperties().stacksTo(1));
private static Item.Properties printoutProperties() {
return properties().stacksTo(1).component(DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
@@ -314,15 +320,15 @@ public final class ModRegistry {
public static final RegistryEntry PRINTED_BOOK = register("printed_book",
PrintoutItem::new, Items::printoutProperties);
- public static final RegistryEntry SPEAKER = ofBlock(Blocks.SPEAKER, BlockItem::new);
- public static final RegistryEntry DISK_DRIVE = ofBlock(Blocks.DISK_DRIVE, BlockItem::new);
- public static final RegistryEntry PRINTER = ofBlock(Blocks.PRINTER, BlockItem::new);
- public static final RegistryEntry MONITOR_NORMAL = ofBlock(Blocks.MONITOR_NORMAL, BlockItem::new);
- public static final RegistryEntry MONITOR_ADVANCED = ofBlock(Blocks.MONITOR_ADVANCED, BlockItem::new);
- public static final RegistryEntry WIRELESS_MODEM_NORMAL = ofBlock(Blocks.WIRELESS_MODEM_NORMAL, BlockItem::new);
- public static final RegistryEntry WIRELESS_MODEM_ADVANCED = ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, BlockItem::new);
- public static final RegistryEntry WIRED_MODEM_FULL = ofBlock(Blocks.WIRED_MODEM_FULL, BlockItem::new);
- public static final RegistryEntry REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, BlockItem::new);
+ public static final RegistryEntry SPEAKER = ofBlock(Blocks.SPEAKER, BlockItem::new, properties());
+ public static final RegistryEntry DISK_DRIVE = ofBlock(Blocks.DISK_DRIVE, BlockItem::new, properties());
+ public static final RegistryEntry PRINTER = ofBlock(Blocks.PRINTER, BlockItem::new, properties());
+ public static final RegistryEntry MONITOR_NORMAL = ofBlock(Blocks.MONITOR_NORMAL, BlockItem::new, properties());
+ public static final RegistryEntry MONITOR_ADVANCED = ofBlock(Blocks.MONITOR_ADVANCED, BlockItem::new, properties());
+ public static final RegistryEntry WIRELESS_MODEM_NORMAL = ofBlock(Blocks.WIRELESS_MODEM_NORMAL, BlockItem::new, properties());
+ public static final RegistryEntry WIRELESS_MODEM_ADVANCED = ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, BlockItem::new, properties());
+ public static final RegistryEntry WIRED_MODEM_FULL = ofBlock(Blocks.WIRED_MODEM_FULL, BlockItem::new, properties());
+ public static final RegistryEntry REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, BlockItem::new, properties());
public static final RegistryEntry CABLE = register("cable",
p -> new CableBlockItem.Cable(Blocks.CABLE.get(), p), properties().useBlockDescriptionPrefix());
@@ -340,8 +346,8 @@ public final class ModRegistry {
/**
* The id of a computer.
*/
- public static final RegistryEntry> COMPUTER_ID = register("computer_id", b -> b
- .persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
+ public static final RegistryEntry> COMPUTER_ID = register("computer_id", b -> b
+ .persistent(NonNegativeId.Computer.CODEC).networkSynchronized(NonNegativeId.Computer.STREAM_CODEC)
);
/**
@@ -422,8 +428,8 @@ public final class ModRegistry {
/**
* The id of a disk.
*/
- public static final RegistryEntry> DISK_ID = register("disk_id", b -> b
- .persistent(NonNegativeId.CODEC).networkSynchronized(NonNegativeId.STREAM_CODEC)
+ public static final RegistryEntry> DISK_ID = register("disk_id", b -> b
+ .persistent(NonNegativeId.Disk.CODEC).networkSynchronized(NonNegativeId.Disk.STREAM_CODEC)
);
/**
@@ -597,8 +603,8 @@ public final class ModRegistry {
out.accept(Items.PRINTED_BOOK.get());
out.accept(Items.DISK_DRIVE.get());
- for (var colour = 0; colour < 16; colour++) {
- out.accept(DataComponentUtil.createStack(Items.DISK.get(), net.minecraft.core.component.DataComponents.DYED_COLOR, new DyedItemColor(Colour.VALUES[colour].getHex(), false)));
+ for (var colour : DyeColor.values()) {
+ out.accept(DataComponentUtil.createDyedStack(Items.DISK.get(), colour.getTextureDiffuseColor()));
}
})
.build());
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java b/projects/common/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java
index 81f60cc0f..aa058c48c 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java
@@ -182,10 +182,7 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder s
- .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, text))
- .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")))
+ .withClickEvent(new ClickEvent.CopyToClipboard(text))
+ .withHoverEvent(new HoverEvent.ShowText(Component.translatable("gui.computercraft.tooltip.copy")))
);
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java
index 98436c763..7b7b2a8fc 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/common/ClearColourRecipe.java
@@ -6,7 +6,6 @@ package dan200.computercraft.shared.common;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.shared.ModRegistry;
-import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
@@ -60,7 +59,9 @@ public final class ClearColourRecipe extends CustomRecipe {
if (colourable.isEmpty()) return ItemStack.EMPTY;
- return DataComponentUtil.createResult(colourable, DataComponents.DYED_COLOR, null);
+ var result = colourable.copyWithCount(1);
+ result.remove(DataComponents.DYED_COLOR);
+ return result;
}
@Override
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java
index cda3a7e21..ed01b3821 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java
@@ -10,9 +10,7 @@ import dan200.computercraft.shared.util.ColourTracker;
import dan200.computercraft.shared.util.ColourUtils;
import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.HolderLookup;
-import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CraftingInput;
import net.minecraft.world.item.crafting.CustomRecipe;
@@ -66,8 +64,7 @@ public final class ColourableRecipe extends CustomRecipe {
return colourable.isEmpty()
? ItemStack.EMPTY
- : DataComponentUtil.createResult(colourable, DataComponents.DYED_COLOR, new DyedItemColor(tracker.getColour(), false));
-
+ : DataComponentUtil.setDyeColour(colourable.copyWithCount(1), tracker.getColour());
}
@Override
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/common/HorizontalContainerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/common/HorizontalContainerBlock.java
index 99cb268a3..f08758c1b 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/common/HorizontalContainerBlock.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/common/HorizontalContainerBlock.java
@@ -6,7 +6,6 @@ package dan200.computercraft.shared.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
-import net.minecraft.world.Containers;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -60,12 +59,6 @@ public abstract class HorizontalContainerBlock extends BaseEntityBlock {
return InteractionResult.CONSUME;
}
- @Override
- protected final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
- Containers.dropContentsOnDestroy(state, newState, level, pos);
- super.onRemove(state, level, pos, newState, isMoving);
- }
-
@Override
protected final boolean hasAnalogOutputSignal(BlockState pState) {
return true;
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java
index 4b7d22b00..d93a782d7 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java
@@ -21,10 +21,10 @@ import dan200.computercraft.shared.util.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
+import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.Container;
import net.minecraft.world.LockCode;
@@ -166,16 +166,16 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
protected void loadServer(CompoundTag nbt, HolderLookup.Provider registries) {
// Load ID, label and power state
- computerID = nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1;
- label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
- storageCapacity = nbt.contains(NBT_CAPACITY, Tag.TAG_ANY_NUMERIC) ? nbt.getLong(NBT_CAPACITY) : -1;
- on = startOn = nbt.getBoolean(NBT_ON);
+ computerID = nbt.getIntOr(NBT_ID, -1);
+ label = nbt.getStringOr(NBT_LABEL, null);
+ storageCapacity = nbt.getLongOr(NBT_CAPACITY, -1);
+ on = startOn = nbt.getBooleanOr(NBT_ON, false);
lockCode = LockCode.fromTag(nbt, registries);
}
@Override
- protected void applyImplicitComponents(DataComponentInput component) {
+ protected void applyImplicitComponents(DataComponentGetter component) {
super.applyImplicitComponents(component);
label = DataComponentUtil.getCustomName(component.get(DataComponents.CUSTOM_NAME));
computerID = NonNegativeId.getId(component.get(ModRegistry.DataComponents.COMPUTER_ID.get()));
@@ -197,7 +197,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
*/
@OverridingMethodsMustInvokeSuper
protected void collectSafeComponents(DataComponentMap.Builder builder) {
- builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.of(computerID));
+ builder.set(ModRegistry.DataComponents.COMPUTER_ID.get(), computerID < 0 ? null : new NonNegativeId.Computer(computerID));
builder.set(DataComponents.CUSTOM_NAME, label == null ? null : Component.literal(label));
builder.set(ModRegistry.DataComponents.STORAGE_CAPACITY.get(), storageCapacity > 0 ? new StorageCapacity(storageCapacity) : null);
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java
index e4b670ad3..2ce63f807 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java
@@ -96,30 +96,30 @@ public class NetworkedTerminal extends Terminal {
}
public synchronized void readFromNBT(CompoundTag nbt) {
- cursorX = nbt.getInt("term_cursorX");
- cursorY = nbt.getInt("term_cursorY");
- cursorBlink = nbt.getBoolean("term_cursorBlink");
- cursorColour = nbt.getInt("term_textColour");
- cursorBackgroundColour = nbt.getInt("term_bgColour");
+ cursorX = nbt.getIntOr("term_cursorX", 0);
+ cursorY = nbt.getIntOr("term_cursorY", 0);
+ cursorBlink = nbt.getBooleanOr("term_cursorBlink", false);
+ cursorColour = nbt.getIntOr("term_textColour", 0);
+ cursorBackgroundColour = nbt.getIntOr("term_bgColour", 0);
for (var n = 0; n < height; n++) {
text[n].fill(' ');
if (nbt.contains("term_text_" + n)) {
- text[n].write(nbt.getString("term_text_" + n));
+ text[n].write(nbt.getStringOr("term_text_" + n, ""));
}
textColour[n].fill(BASE_16.charAt(cursorColour));
if (nbt.contains("term_textColour_" + n)) {
- textColour[n].write(nbt.getString("term_textColour_" + n));
+ textColour[n].write(nbt.getStringOr("term_textColour_" + n, ""));
}
backgroundColour[n].fill(BASE_16.charAt(cursorBackgroundColour));
if (nbt.contains("term_textBgColour_" + n)) {
- backgroundColour[n].write(nbt.getString("term_textBgColour_" + n));
+ backgroundColour[n].write(nbt.getStringOr("term_textBgColour_" + n, ""));
}
}
if (nbt.contains("term_palette")) {
- var rgb8 = nbt.getIntArray("term_palette");
- if (rgb8.length == Palette.PALETTE_SIZE) {
+ var rgb8 = nbt.getIntArray("term_palette").orElse(null);
+ if (rgb8 != null && rgb8.length == Palette.PALETTE_SIZE) {
for (var i = 0; i < Palette.PALETTE_SIZE; i++) {
var colours = Palette.decodeRGB8(rgb8[i]);
palette.setColour(i, colours[0], colours[1], colours[2]);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java b/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java
index dfcfd8caa..001cff0f0 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/details/ItemDetails.java
@@ -61,7 +61,7 @@ public class ItemDetails {
if (!enchants.isEmpty()) data.put("enchantments", enchants);
var unbreakable = stack.get(DataComponents.UNBREAKABLE);
- if (unbreakable != null && unbreakable.showInTooltip()) data.put("unbreakable", true);
+ if (unbreakable != null) data.put("unbreakable", true);
}
/**
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlock.java
index 696fe73a3..b06782d5b 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlock.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlock.java
@@ -112,18 +112,7 @@ public class CustomLecternBlock extends LecternBlock {
super.tick(state, level, pos, random);
}
- @Override
- public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
- if (state.is(newState.getBlock())) return;
-
- if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity lectern) {
- dropItem(level, pos, state, lectern.getItem().copy());
- }
-
- super.onRemove(state, level, pos, newState, isMoving);
- }
-
- private static void dropItem(Level level, BlockPos pos, BlockState state, ItemStack stack) {
+ static void dropItem(Level level, BlockPos pos, BlockState state, ItemStack stack) {
if (stack.isEmpty()) return;
var direction = state.getValue(FACING);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlockEntity.java
index f49f1d7c4..e968d3964 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/lectern/CustomLecternBlockEntity.java
@@ -16,7 +16,6 @@ import dan200.computercraft.shared.util.BlockEntityHelpers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.Tag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
@@ -34,6 +33,8 @@ import net.minecraft.world.level.block.state.BlockState;
import java.util.AbstractList;
import java.util.List;
+import static dan200.computercraft.shared.lectern.CustomLecternBlock.dropItem;
+
/**
* The block entity for our {@link CustomLecternBlock}.
*
@@ -100,12 +101,17 @@ public final class CustomLecternBlockEntity extends BlockEntity {
if (getLevel() != null) LecternBlock.signalPageChange(getLevel(), getBlockPos(), getBlockState());
}
+ @Override
+ public void preRemoveSideEffects(BlockPos pos, BlockState state) {
+ if (level != null) dropItem(level, pos, state, getItem().copy());
+ }
+
@Override
public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
- item = tag.contains(NBT_ITEM, Tag.TAG_COMPOUND) ? ItemStack.parseOptional(registries, tag.getCompound(NBT_ITEM)) : ItemStack.EMPTY;
- page = tag.getInt(NBT_PAGE);
+ item = tag.getCompound(NBT_ITEM).flatMap(x -> ItemStack.parse(registries, x)).orElse(ItemStack.EMPTY);
+ page = tag.getIntOr(NBT_PAGE, 0);
itemChanged();
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java b/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java
index 677d61242..b80b93741 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java
@@ -10,6 +10,7 @@ import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.util.DataComponentUtil;
+import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.util.NonNegativeId;
import dan200.computercraft.shared.util.StorageCapacity;
import net.minecraft.core.HolderLookup;
@@ -18,25 +19,28 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
+import java.util.function.IntFunction;
import java.util.function.Supplier;
/**
* Media that provides a {@link Mount}.
+ *
+ * @param The type of media (disk/computer) we're mounting.
*/
-public final class MountMedia implements IMedia {
+public final class MountMedia implements IMedia {
/**
* A {@link MountMedia} implementation for {@linkplain ModRegistry.DataComponents#COMPUTER_ID computers}.
*/
- public static final IMedia COMPUTER = new MountMedia("computer", ModRegistry.DataComponents.COMPUTER_ID, false, ConfigSpec.computerSpaceLimit);
+ public static final IMedia COMPUTER = new MountMedia<>(IDAssigner.COMPUTER, ModRegistry.DataComponents.COMPUTER_ID, null, ConfigSpec.computerSpaceLimit);
/**
* A {@link MountMedia} implementation for {@linkplain ModRegistry.Items#DISK disks}.
*/
- public static final IMedia DISK = new MountMedia("disk", ModRegistry.DataComponents.DISK_ID, true, ConfigSpec.floppySpaceLimit);
+ public static final IMedia DISK = new MountMedia<>("disk", ModRegistry.DataComponents.DISK_ID, NonNegativeId.Disk::new, ConfigSpec.floppySpaceLimit);
private final String subPath;
- private final Supplier> id;
- private final boolean createId;
+ private final Supplier> id;
+ private final @Nullable IntFunction createId;
private final Supplier defaultCapacity;
/**
@@ -49,8 +53,8 @@ public final class MountMedia implements IMedia {
*/
public MountMedia(
String subPath,
- Supplier> id,
- boolean createId,
+ Supplier> id,
+ @Nullable IntFunction createId,
Supplier defaultCapacity
) {
this.subPath = subPath;
@@ -72,8 +76,8 @@ public final class MountMedia implements IMedia {
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
- var id = createId
- ? NonNegativeId.getOrCreate(level.getServer(), stack, this.id.get(), subPath)
+ var id = createId != null
+ ? NonNegativeId.getOrCreate(level.getServer(), stack, this.id.get(), createId, subPath)
: NonNegativeId.getId(stack.get(this.id.get()));
if (id < 0) return null;
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/PrintoutMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/media/PrintoutMenu.java
index fb6960394..0205fdbef 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/PrintoutMenu.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/PrintoutMenu.java
@@ -81,7 +81,7 @@ public class PrintoutMenu extends AbstractContainerMenu {
var currentItem = currentStack.getItem();
var slot = switch (hand) {
- case MAIN_HAND -> player.getInventory().selected;
+ case MAIN_HAND -> player.getInventory().getSelectedSlot();
case OFF_HAND -> Inventory.SLOT_OFFHAND;
};
return new PrintoutMenu(
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java
index db7219e38..2113b02e8 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java
@@ -9,6 +9,7 @@ import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.context.UseOnContext;
+
/**
* An item that can be shift-right-clicked into a {@link DiskDriveBlock}.
*/
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutData.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutData.java
index bb650af33..24c893df1 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutData.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutData.java
@@ -11,6 +11,7 @@ import dan200.computercraft.api.media.PrintoutContents;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.ModRegistry;
import io.netty.buffer.ByteBuf;
+import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentHolder;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
@@ -77,7 +78,7 @@ public record PrintoutData(String title, List lines) implements PrintoutCo
);
@Override
- public void addToTooltip(Item.TooltipContext context, Consumer out, TooltipFlag flag) {
+ public void addToTooltip(Item.TooltipContext context, Consumer out, TooltipFlag flag, DataComponentGetter components) {
if (!title().isEmpty()) out.accept(Component.literal(title()));
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutItem.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutItem.java
index 4f476e83f..3700ea009 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutItem.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/PrintoutItem.java
@@ -14,6 +14,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
+
public class PrintoutItem extends Item {
public PrintoutItem(Properties settings) {
super(settings);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDisk.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDisk.java
index 40af4d094..42c5bf0dd 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDisk.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDisk.java
@@ -7,6 +7,7 @@ package dan200.computercraft.shared.media.items;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.shared.ModRegistry;
+import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentHolder;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
@@ -44,7 +45,7 @@ public record TreasureDisk(String name, String path) implements TooltipProvider
}
@Override
- public void addToTooltip(Item.TooltipContext context, Consumer out, TooltipFlag flags) {
+ public void addToTooltip(Item.TooltipContext context, Consumer out, TooltipFlag flags, DataComponentGetter components) {
out.accept(Component.literal(name()));
}
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
index a54067e92..baa8be346 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
@@ -15,7 +15,6 @@ import dan200.computercraft.shared.util.ColourTracker;
import dan200.computercraft.shared.util.ColourUtils;
import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.HolderLookup;
-import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
@@ -24,7 +23,6 @@ import net.minecraft.world.entity.player.StackedItemContents;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
-import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.item.crafting.CraftingInput;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.PlacementInfo;
@@ -70,15 +68,10 @@ public class DiskRecipe extends AbstractCraftingRecipe {
var dyes = ColourUtils.DYES;
List out = new ArrayList<>(dyes.size());
for (var i = 0; i < dyes.size(); i++) {
- var tracker = new ColourTracker();
- tracker.addColour(DyeColor.byId(i));
-
out.add(new ShapelessCraftingRecipeDisplay(
Stream.concat(ingredients.stream(), Stream.of(Ingredient.of(BuiltInRegistries.ITEM.getOrThrow(dyes.get(i)))))
.map(Ingredient::display).toList(),
- new SlotDisplay.ItemStackSlotDisplay(DataComponentUtil.createStack(
- ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(tracker.getColour(), false)
- )),
+ new SlotDisplay.ItemStackSlotDisplay(DataComponentUtil.createDyedStack(ModRegistry.Items.DISK.get(), DyeColor.byId(i).getTextureDiffuseColor())),
new SlotDisplay.ItemSlotDisplay(Items.CRAFTING_TABLE)
));
}
@@ -114,7 +107,7 @@ public class DiskRecipe extends AbstractCraftingRecipe {
if (dye != null) tracker.addColour(dye);
}
- return DataComponentUtil.createStack(ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(tracker.hasColour() ? tracker.getColour() : Colour.BLUE.getHex(), false));
+ return DataComponentUtil.createDyedStack(ModRegistry.Items.DISK.get(), tracker.getColourOr(Colour.BLUE.getHex()));
}
@Override
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java
index fc54a8b7d..8af9f9227 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveBlockEntity.java
@@ -116,7 +116,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity imp
@Override
public void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) {
super.loadAdditional(nbt, registries);
- setDiskStack(nbt.contains(NBT_ITEM) ? ItemStack.parseOptional(registries, nbt.getCompound(NBT_ITEM)) : ItemStack.EMPTY);
+ setDiskStack(nbt.getCompound(NBT_ITEM).flatMap(x -> ItemStack.parse(registries, x)).orElse(ItemStack.EMPTY));
}
@Override
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
index 965a4cc63..29f129106 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
@@ -12,7 +12,6 @@ import dan200.computercraft.shared.platform.ComponentAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.Tag;
import net.minecraft.world.level.Level;
import org.jspecify.annotations.Nullable;
@@ -104,11 +103,8 @@ public final class WiredModemLocalPeripheral {
}
public void read(CompoundTag tag, String suffix) {
- id = tag.contains(NBT_PERIPHERAL_ID + suffix, Tag.TAG_ANY_NUMERIC)
- ? tag.getInt(NBT_PERIPHERAL_ID + suffix) : -1;
-
- type = tag.contains(NBT_PERIPHERAL_TYPE + suffix, Tag.TAG_STRING)
- ? tag.getString(NBT_PERIPHERAL_TYPE + suffix) : null;
+ id = tag.getIntOr(NBT_PERIPHERAL_ID + suffix, -1);
+ type = tag.getStringOr(NBT_PERIPHERAL_TYPE + suffix, null);
}
@Nullable
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java
index 162ad3318..7c3ca4df6 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlock.java
@@ -83,15 +83,6 @@ public class MonitorBlock extends HorizontalDirectionalBlock implements EntityBl
.setValue(ORIENTATION, orientation);
}
- @Override
- protected final void onRemove(BlockState block, Level world, BlockPos pos, BlockState replace, boolean bool) {
- if (block.getBlock() == replace.getBlock()) return;
-
- var tile = world.getBlockEntity(pos);
- super.onRemove(block, world, pos, replace, bool);
- if (tile instanceof MonitorBlockEntity generic) generic.destroy();
- }
-
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {
var te = world.getBlockEntity(pos);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java
index 1e8e94d96..a3e2c9d88 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java
@@ -57,6 +57,12 @@ public class MonitorBlockEntity extends BlockEntity {
private boolean needsUpdate = false;
private boolean needsValidating = false;
+ /**
+ * Whether this monitor is in the process of being removed (see {@link #preRemoveSideEffects(BlockPos, BlockState)},
+ * and so should be ignored.
+ */
+ private boolean isRemoving = false;
+
// MonitorWatcher state.
boolean enqueued;
@Nullable
@@ -82,13 +88,16 @@ public class MonitorBlockEntity extends BlockEntity {
@Override
public void clearRemoved() {
super.clearRemoved();
+ isRemoving = false;
needsValidating = true; // Same, tbh
TickScheduler.schedule(tickToken);
}
- void destroy() {
- // TODO: Call this before using the block
- if (!getLevel().isClientSide) contractNeighbours();
+ @Override
+ public void preRemoveSideEffects(BlockPos blockPos, BlockState blockState) {
+ super.preRemoveSideEffects(blockPos, blockState);
+ isRemoving = true;
+ if (level != null && !getLevel().isClientSide) contractNeighbours();
}
@Override
@@ -113,10 +122,10 @@ public class MonitorBlockEntity extends BlockEntity {
var oldXIndex = xIndex;
var oldYIndex = yIndex;
- xIndex = nbt.getInt(NBT_X);
- yIndex = nbt.getInt(NBT_Y);
- width = nbt.getInt(NBT_WIDTH);
- height = nbt.getInt(NBT_HEIGHT);
+ xIndex = nbt.getIntOr(NBT_X, 0);
+ yIndex = nbt.getIntOr(NBT_Y, 0);
+ width = nbt.getIntOr(NBT_WIDTH, 1);
+ height = nbt.getIntOr(NBT_HEIGHT, 1);
if (level != null && level.isClientSide) onClientLoad(oldXIndex, oldYIndex);
}
@@ -245,13 +254,11 @@ public class MonitorBlockEntity extends BlockEntity {
public Direction getDirection() {
// Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's
// fun problems with the block being missing on the client.
- var state = getBlockState();
- return state.hasProperty(MonitorBlock.FACING) ? state.getValue(MonitorBlock.FACING) : Direction.NORTH;
+ return getBlockState().getValueOrElse(MonitorBlock.FACING, Direction.NORTH);
}
public Direction getOrientation() {
- var state = getBlockState();
- return state.hasProperty(MonitorBlock.ORIENTATION) ? state.getValue(MonitorBlock.ORIENTATION) : Direction.NORTH;
+ return getBlockState().getValueOrElse(MonitorBlock.ORIENTATION, Direction.NORTH);
}
public Direction getFront() {
@@ -286,7 +293,10 @@ public class MonitorBlockEntity extends BlockEntity {
}
boolean isCompatible(MonitorBlockEntity other) {
- return advanced == other.advanced && getOrientation() == other.getOrientation() && getDirection() == other.getDirection();
+ return !other.isRemoved() && !other.isRemoving
+ && advanced == other.advanced
+ && getOrientation() == other.getOrientation()
+ && getDirection() == other.getDirection();
}
/**
@@ -303,8 +313,7 @@ public class MonitorBlockEntity extends BlockEntity {
var world = getLevel();
if (world == null || !world.isLoaded(pos)) return MonitorState.UNLOADED;
- var tile = world.getBlockEntity(pos);
- if (!(tile instanceof MonitorBlockEntity monitor)) return MonitorState.MISSING;
+ if (!(world.getBlockEntity(pos) instanceof MonitorBlockEntity monitor)) return MonitorState.MISSING;
return isCompatible(monitor) ? MonitorState.present(monitor) : MonitorState.MISSING;
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlockEntity.java
index da5f527fc..f6836ac58 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterBlockEntity.java
@@ -60,8 +60,8 @@ public final class PrinterBlockEntity extends AbstractContainerBlockEntity imple
// Read page
synchronized (page) {
- printing = nbt.getBoolean(NBT_PRINTING);
- pageTitle = nbt.getString(NBT_PAGE_TITLE);
+ printing = nbt.getBooleanOr(NBT_PRINTING, false);
+ pageTitle = nbt.getStringOr(NBT_PAGE_TITLE, "");
page.readFromNBT(nbt);
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java
index b09bea1ac..a1117174f 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java
@@ -10,7 +10,8 @@ import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.impl.PocketUpgrades;
-import net.minecraft.core.NonNullList;
+import net.minecraft.world.Container;
+import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.Nullable;
@@ -69,10 +70,12 @@ public class PocketAPI implements ILuaAPI {
// Attempt to find the upgrade, starting in the main segment, and then looking in the opposite
// one. We start from the position the item is currently in and loop round to the start.
- var newUpgrade = findUpgrade(inventory.items, inventory.selected, previousUpgrade);
- if (newUpgrade == null) {
- newUpgrade = findUpgrade(inventory.offhand, 0, previousUpgrade);
+ UpgradeData newUpgrade = null;
+ for (var i = 0; i < Inventory.INVENTORY_SIZE; i++) {
+ newUpgrade = findUpgrade(inventory, (i + inventory.getSelectedSlot()) % Inventory.INVENTORY_SIZE, previousUpgrade);
+ if (newUpgrade != null) break;
}
+ if (newUpgrade == null) newUpgrade = findUpgrade(inventory, Inventory.SLOT_OFFHAND, previousUpgrade);
if (newUpgrade == null) return new Object[]{ false, "Cannot find a valid upgrade" };
// Remove the current upgrade
@@ -113,21 +116,18 @@ public class PocketAPI implements ILuaAPI {
}
}
- private @Nullable UpgradeData findUpgrade(NonNullList inv, int start, @Nullable UpgradeData previous) {
- for (var i = 0; i < inv.size(); i++) {
- var invStack = inv.get((i + start) % inv.size());
- if (!invStack.isEmpty()) {
- var newUpgrade = PocketUpgrades.instance().get(pocket.getLevel().registryAccess(), invStack);
+ private @Nullable UpgradeData findUpgrade(Container inv, int slot, @Nullable UpgradeData previous) {
+ var invStack = inv.getItem(slot);
+ if (invStack.isEmpty()) return null;
- if (newUpgrade != null && !Objects.equals(newUpgrade, previous)) {
- // Consume an item from this stack and exit the loop
- invStack = invStack.copy();
- invStack.shrink(1);
- inv.set((i + start) % inv.size(), invStack.isEmpty() ? ItemStack.EMPTY : invStack);
+ var newUpgrade = PocketUpgrades.instance().get(pocket.getLevel().registryAccess(), invStack);
+ if (newUpgrade != null && !Objects.equals(newUpgrade, previous)) {
+ // Consume an item from this stack and exit the loop
+ invStack = invStack.copy();
+ invStack.shrink(1);
+ inv.setItem(slot, invStack.isEmpty() ? ItemStack.EMPTY : invStack);
- return newUpgrade;
- }
- }
+ return newUpgrade;
}
return null;
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketBrain.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketBrain.java
index 96dc50891..b619309f6 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketBrain.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketBrain.java
@@ -12,12 +12,12 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
import dan200.computercraft.shared.network.server.ServerNetworking;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
+import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponents;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
@@ -93,7 +93,11 @@ public final class PocketBrain implements IPocketAccess {
if (!dirty) return false;
this.dirty = false;
- stack.set(DataComponents.DYED_COLOR, colour == -1 ? null : new DyedItemColor(colour, false));
+ if (colour == -1) {
+ stack.remove(DataComponents.DYED_COLOR);
+ } else {
+ DataComponentUtil.setDyeColour(stack, colour);
+ }
PocketComputerItem.setUpgrade(stack, upgrade);
return true;
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketHolder.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketHolder.java
index 2682cae95..6aa1b3787 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketHolder.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketHolder.java
@@ -12,6 +12,8 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.phys.Vec3;
@@ -107,6 +109,24 @@ public sealed interface PocketHolder {
}
}
+ /**
+ * A pocket computer in a {@link LivingEntity}'s slot.
+ *
+ * @param entity The current player.
+ * @param slot The slot the pocket computer is in.
+ */
+ record LivingEntityHolder(LivingEntity entity, EquipmentSlot slot) implements EntityHolder {
+ @Override
+ public boolean isValid(ServerComputer computer) {
+ return entity().isAlive() && PocketComputerItem.isServerComputer(computer, entity().getItemBySlot(slot()));
+ }
+
+ @Override
+ public void setChanged() {
+ // TODO: What can we do?
+ }
+ }
+
/**
* A pocket computer in an {@link ItemEntity}.
*
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java
index 33790c2d4..d3bf11ecb 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java
@@ -31,6 +31,8 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
@@ -102,16 +104,13 @@ public class PocketComputerItem extends Item {
}
@Override
- public void inventoryTick(ItemStack stack, Level world, Entity entity, int compartmentSlot, boolean selected) {
- // This (in vanilla at least) is only called for players. Don't bother to handle other entities.
- if (world.isClientSide || !(entity instanceof ServerPlayer player)) return;
-
- // Find the actual slot the item exists in, aborting if it can't be found.
- var slot = InventoryUtil.getInventorySlotFromCompartment(player, compartmentSlot, stack);
- if (slot < 0) return;
-
- // If we're in the inventory, create a computer and keep it alive.
- tick(stack, new PocketHolder.PlayerHolder(player, slot), false);
+ public void inventoryTick(ItemStack stack, ServerLevel level, Entity entity, @Nullable EquipmentSlot slot) {
+ if (entity instanceof ServerPlayer player) {
+ var invSlot = InventoryUtil.findItemInInventory(player.getInventory(), stack);
+ if (invSlot != -1) tick(stack, new PocketHolder.PlayerHolder(player, invSlot), false);
+ } else if (slot != null && entity instanceof LivingEntity living) {
+ tick(stack, new PocketHolder.LivingEntityHolder(living, slot), true);
+ }
}
@ForgeOverride
@@ -200,7 +199,7 @@ public class PocketComputerItem extends Item {
}
}
- var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), IDAssigner.COMPUTER);
+ var computerID = NonNegativeId.getOrCreate(level.getServer(), stack, ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId.Computer::new, IDAssigner.COMPUTER);
var brain = new PocketBrain(
holder, getUpgradeWithData(stack), DyedItemColor.getOrDefault(stack, -1),
ServerComputer.properties(computerID, getFamily())
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java
index cfacf1e07..f0501269c 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java
@@ -7,13 +7,13 @@ package dan200.computercraft.shared.turtle;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.shared.platform.ContainerTransfer;
import dan200.computercraft.shared.platform.PlatformHelper;
+import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
+import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
-import java.util.function.Function;
-
public class TurtleUtil {
/**
* Get a view of the turtle's inventory starting at the currently selected slot. This should be used when
@@ -43,6 +43,10 @@ public class TurtleUtil {
* @param stack The stack to store.
*/
public static void storeItemOrDrop(ITurtleAccess turtle, ItemStack stack) {
+ storeItemOrDrop(turtle, turtle.getInventory(), stack);
+ }
+
+ private static void storeItemOrDrop(ITurtleAccess turtle, Container container, ItemStack stack) {
if (stack.isEmpty()) return;
if (turtle.isRemoved()) {
WorldUtil.dropItemStack(turtle.getLevel(), turtle.getPosition(), null, stack);
@@ -50,18 +54,32 @@ public class TurtleUtil {
}
// Put the remainder back in the turtle
- var remainder = InventoryUtil.storeItemsFromOffset(turtle.getInventory(), stack, turtle.getSelectedSlot());
+ var remainder = InventoryUtil.storeItemsFromOffset(container, stack, turtle.getSelectedSlot());
if (remainder.isEmpty()) return;
WorldUtil.dropItemStack(turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite(), remainder);
}
- public static Function dropConsumer(ITurtleAccess turtle) {
- return stack -> turtle.isRemoved() ? stack : InventoryUtil.storeItemsFromOffset(turtle.getInventory(), stack, turtle.getSelectedSlot());
+ /**
+ * Stop a {@link DropConsumer}, and sync the items back to the inventory.
+ *
+ * @param turtle The turtle to store drops to.
+ */
+ public static void stopConsuming(ITurtleAccess turtle) {
+ for (var stack : DropConsumer.stop()) storeItemOrDrop(turtle, stack);
}
- public static void stopConsuming(ITurtleAccess turtle) {
- var direction = turtle.isRemoved() ? null : turtle.getDirection().getOpposite();
- DropConsumer.clearAndDrop(turtle.getLevel(), turtle.getPosition(), direction);
+ /**
+ * Stop a {@link DropConsumer}, and sync the items back to the {@link TurtlePlayer} inventory.
+ *
+ * When using {@link TurtlePlayer#loadInventory(ITurtleAccess)}/{@link TurtlePlayer#unloadInventory(ITurtleAccess)},
+ * changes to the turtle's inventory are overridden. This means items must be stored to the player's
+ * inventory, not the turtle's.
+ *
+ * @param turtle The turtle performing this action.
+ * @param player The turtle player to store items back to.
+ */
+ public static void stopConsumingPlayer(ITurtleAccess turtle, TurtlePlayer player) {
+ for (var stack : DropConsumer.stop()) storeItemOrDrop(turtle, player.player().getInventory(), stack);
}
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java
index 800a1cf5f..27a58ec53 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java
@@ -17,7 +17,6 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.component.DataComponents;
import net.minecraft.util.RandomSource;
-import net.minecraft.world.Containers;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
@@ -122,21 +121,6 @@ public class TurtleBlock extends AbstractComputerBlock implem
return super.updateShape(state, level, ticker, pos, side, otherPos, neighborState, randomSource);
}
- @Override
- protected final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
- if (state.is(newState.getBlock())) return;
-
- // Most blocks drop items and then remove the BE. However, if a turtle is consuming drops right now, that can
- // lead to loops where it tries to insert an item back into the inventory. To prevent this, take a reference to
- // the turtle BE now, remove it, and then drop the items.
- var turtle = !level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity t && !t.hasMoved()
- ? t : null;
-
- super.onRemove(state, level, pos, newState, isMoving);
-
- if (turtle != null) Containers.dropContents(level, pos, turtle);
- }
-
@Override
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity entity, ItemStack stack) {
super.setPlacedBy(level, pos, state, entity, stack);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java
index 3ae340260..27507d4e5 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java
@@ -28,6 +28,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
+import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
@@ -40,6 +41,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.DyedItemColor;
+import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
@@ -119,6 +121,11 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
protected void updateBlockState(ComputerState newState) {
}
+ @Override
+ public void preRemoveSideEffects(BlockPos blockPos, BlockState blockState) {
+ if (!hasMoved()) super.preRemoveSideEffects(blockPos, blockState);
+ }
+
@Override
public void neighborChanged() {
if (moveState == MoveState.NOT_MOVED) super.neighborChanged();
@@ -157,7 +164,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
}
@Override
- protected void applyImplicitComponents(DataComponentInput component) {
+ protected void applyImplicitComponents(DataComponentGetter component) {
super.applyImplicitComponents(component);
var colour = component.get(DataComponents.DYED_COLOR);
@@ -173,7 +180,10 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
protected void collectSafeComponents(DataComponentMap.Builder builder) {
super.collectSafeComponents(builder);
- builder.set(DataComponents.DYED_COLOR, brain.getColour() == -1 ? null : new DyedItemColor(brain.getColour(), false));
+ if (brain.getColour() != -1) {
+ builder.set(DataComponents.DYED_COLOR, new DyedItemColor(brain.getColour()));
+ builder.set(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT.withHidden(DataComponents.DYED_COLOR, true));
+ }
builder.set(ModRegistry.DataComponents.OVERLAY.get(), brain.getOverlay());
builder.set(ModRegistry.DataComponents.FUEL.get(), brain.getFuelLevel());
builder.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), withPersistedData(brain.getUpgradeWithData(TurtleSide.LEFT)));
@@ -300,7 +310,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
@Override
public void loadClient(CompoundTag nbt, HolderLookup.Provider registries) {
super.loadClient(nbt, registries);
- label = nbt.contains(NBT_LABEL) ? nbt.getString(NBT_LABEL) : null;
+ label = nbt.getStringOr(NBT_LABEL, null);
brain.readDescription(nbt, registries);
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
index 396bd7826..73bc9d056 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
@@ -32,7 +32,6 @@ import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.Container;
@@ -133,8 +132,8 @@ public class TurtleBrain implements TurtleAccessInternal {
*/
private void readCommon(CompoundTag nbt, HolderLookup.Provider registries) {
// Read fields
- colourHex = nbt.contains(NBT_COLOUR) ? nbt.getInt(NBT_COLOUR) : -1;
- fuelLevel = nbt.contains(NBT_FUEL) ? nbt.getInt(NBT_FUEL) : 0;
+ colourHex = nbt.getIntOr(NBT_COLOUR, -1);
+ fuelLevel = nbt.getIntOr(NBT_FUEL, 0);
overlay = nbt.contains(NBT_OVERLAY) ? NBTUtil.decodeFrom(TurtleOverlay.CODEC, registries, nbt, NBT_OVERLAY) : null;
// Read upgrades
@@ -145,7 +144,7 @@ public class TurtleBrain implements TurtleAccessInternal {
private void writeCommon(CompoundTag nbt, HolderLookup.Provider registries) {
nbt.putInt(NBT_FUEL, fuelLevel);
if (colourHex != -1) nbt.putInt(NBT_COLOUR, colourHex);
- if (overlay != null) NBTUtil.encodeTo(TurtleOverlay.CODEC, registries, nbt, NBT_OVERLAY, overlay);
+ NBTUtil.encodeTo(TurtleOverlay.CODEC, registries, nbt, NBT_OVERLAY, overlay);
// Write upgrades
NBTUtil.encodeTo(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_LEFT_UPGRADE, getUpgradeWithData(TurtleSide.LEFT));
@@ -156,14 +155,14 @@ public class TurtleBrain implements TurtleAccessInternal {
readCommon(nbt, registries);
// Read state
- selectedSlot = nbt.getInt(NBT_SLOT);
+ selectedSlot = nbt.getIntOr(NBT_SLOT, 0);
// Read owner
- if (nbt.contains("Owner", Tag.TAG_COMPOUND)) {
- var owner = nbt.getCompound("Owner");
+ var owner = nbt.getCompound("Owner").orElse(null);
+ if (owner != null) {
owningPlayer = new GameProfile(
- new UUID(owner.getLong("UpperId"), owner.getLong("LowerId")),
- owner.getString("Name")
+ new UUID(owner.getLongOr("UpperId", 0), owner.getLongOr("LowerId", 0)),
+ owner.getStringOr("Name", "")
);
} else {
owningPlayer = null;
@@ -191,7 +190,7 @@ public class TurtleBrain implements TurtleAccessInternal {
readCommon(nbt, registries);
// Animation
- var anim = TurtleAnimation.values()[nbt.getInt("Animation")];
+ var anim = TurtleAnimation.values()[nbt.getIntOr("Animation", 0)];
if (anim != animation &&
anim != TurtleAnimation.WAIT &&
anim != TurtleAnimation.SHORT_WAIT &&
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
index f16de963d..05ab90827 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
@@ -13,7 +13,6 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.turtle.TurtleUtil;
import dan200.computercraft.shared.util.DropConsumer;
-import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -106,9 +105,9 @@ public class TurtlePlaceCommand implements TurtleCommand {
var hitEntity = entityHit.getEntity();
var hitPos = entityHit.getLocation();
- DropConsumer.set(hitEntity, drop -> InventoryUtil.storeItemsFromOffset(turtlePlayer.player().getInventory(), drop, 1));
+ DropConsumer.set(hitEntity);
var placed = PlatformHelper.get().interactWithEntity(turtlePlayer.player(), hitEntity, hitPos);
- TurtleUtil.stopConsuming(turtle);
+ TurtleUtil.stopConsumingPlayer(turtle, turtlePlayer);
return placed;
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
index 1ff756451..ff660a148 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
@@ -134,7 +134,7 @@ public final class TurtlePlayer {
public void loadInventory(ItemStack stack) {
player.getInventory().clearContent();
- player.getInventory().selected = 0;
+ player.getInventory().setSelectedSlot(0);
player.getInventory().setItem(0, stack);
}
@@ -145,7 +145,7 @@ public final class TurtlePlayer {
var slots = turtleInventory.getContainerSize();
// Load up the fake inventory
- inventory.selected = 0;
+ inventory.setSelectedSlot(0);
for (var i = 0; i < slots; i++) {
inventory.setItem(i, turtleInventory.getItem((currentSlot + i) % slots));
}
@@ -160,7 +160,7 @@ public final class TurtlePlayer {
var slots = turtleInventory.getContainerSize();
// Load up the fake inventory
- inventory.selected = 0;
+ inventory.setSelectedSlot(0);
for (var i = 0; i < slots; i++) {
turtleInventory.setItem((currentSlot + i) % slots, inventory.getItem(i));
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
index 1b57e4cdf..764045849 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
@@ -175,7 +175,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
var hitEntity = entityHit.getEntity();
// Start claiming entity drops
- DropConsumer.set(hitEntity, TurtleUtil.dropConsumer(turtle));
+ DropConsumer.set(hitEntity);
// Attack the entity
var result = PlatformHelper.get().canAttackEntity(player, hitEntity);
@@ -285,7 +285,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
if (!breakable.isSuccess()) return breakable;
// And break it!
- DropConsumer.set(level, blockPosition, TurtleUtil.dropConsumer(turtle));
+ DropConsumer.set(level, blockPosition);
var broken = !turtlePlayer.isBlockProtected(level, blockPosition) && turtlePlayer.player().gameMode.destroyBlock(blockPosition);
TurtleUtil.stopConsuming(turtle);
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/ColourTracker.java b/projects/common/src/main/java/dan200/computercraft/shared/util/ColourTracker.java
index e4e6acffc..6bf264649 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/ColourTracker.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/ColourTracker.java
@@ -53,4 +53,8 @@ public class ColourTracker {
return (avgR << 16) | (avgG << 8) | avgB;
}
+
+ public int getColourOr(int fallback) {
+ return hasColour() ? getColour() : fallback;
+ }
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java
index a78d84b32..66e4c04d6 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java
@@ -10,6 +10,7 @@ import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.level.ItemLike;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
@@ -38,14 +39,26 @@ public class DataComponentUtil {
return stack;
}
- public static ItemStack createResult(ItemStack stack, DataComponentType type, @Nullable T value) {
- return set(stack.copyWithCount(1), type, value);
- }
-
public static ItemStack createStack(ItemLike item, DataComponentType type, @Nullable T value) {
return set(new ItemStack(item), type, value);
}
+ /**
+ * Create a stack dyed with a particular colour, but with the colour hidden from the tooltip.
+ *
+ * @param item The item to create the stack from.
+ * @param colour The stack's colour.
+ * @return The newly created stack.
+ */
+ public static ItemStack createDyedStack(ItemLike item, int colour) {
+ return setDyeColour(new ItemStack(item), colour);
+ }
+
+ public static ItemStack setDyeColour(ItemStack stack, int colour) {
+ stack.set(DataComponents.DYED_COLOR, new DyedItemColor(colour));
+ return stack;
+ }
+
/**
* Check a component is present in a {@link DataComponentPatch} and matches the supplied predicate.
*
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/projects/common/src/main/java/dan200/computercraft/shared/util/DropConsumer.java
index 8b5b2daed..3ac304e63 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/DropConsumer.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/DropConsumer.java
@@ -5,7 +5,6 @@
package dan200.computercraft.shared.util;
import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
@@ -15,7 +14,6 @@ import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Function;
import static dan200.computercraft.core.util.Nullability.assertNonNull;
@@ -23,34 +21,30 @@ public final class DropConsumer {
private DropConsumer() {
}
- private static @Nullable Function dropConsumer;
- private static @Nullable List remainingDrops;
+ private static @Nullable List drops;
private static @Nullable Level dropWorld;
private static @Nullable AABB dropBounds;
private static @Nullable Entity dropEntity;
- public static void set(Entity entity, Function consumer) {
- dropConsumer = consumer;
- remainingDrops = new ArrayList<>();
+ public static void set(Entity entity) {
+ drops = new ArrayList<>();
dropEntity = entity;
dropWorld = entity.level();
dropBounds = new AABB(entity.blockPosition()).inflate(2, 2, 2);
}
- public static void set(Level world, BlockPos pos, Function consumer) {
- dropConsumer = consumer;
- remainingDrops = new ArrayList<>(2);
+ public static void set(Level world, BlockPos pos) {
+ drops = new ArrayList<>(2);
dropEntity = null;
dropWorld = world;
dropBounds = new AABB(pos).inflate(2, 2, 2);
}
- public static List clear() {
- var remainingStacks = remainingDrops;
+ public static List stop() {
+ var remainingStacks = drops;
if (remainingStacks == null) throw new IllegalStateException("Not currently capturing");
- dropConsumer = null;
- remainingDrops = null;
+ drops = null;
dropEntity = null;
dropWorld = null;
dropBounds = null;
@@ -58,14 +52,8 @@ public final class DropConsumer {
return remainingStacks;
}
- public static void clearAndDrop(Level world, BlockPos pos, @Nullable Direction direction) {
- var remainingDrops = clear();
- for (var remaining : remainingDrops) WorldUtil.dropItemStack(world, pos, direction, remaining);
- }
-
private static void handleDrops(ItemStack stack) {
- var remaining = assertNonNull(dropConsumer).apply(stack);
- if (!remaining.isEmpty()) assertNonNull(remainingDrops).add(remaining);
+ assertNonNull(drops).add(stack);
}
public static boolean onEntitySpawn(Entity entity) {
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
index 717b53d62..7d442bc24 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
@@ -9,12 +9,9 @@ import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
-import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
@@ -32,30 +29,22 @@ public final class InventoryUtil {
*/
public static int getHandSlot(Player player, InteractionHand hand) {
return switch (hand) {
- case MAIN_HAND -> player.getInventory().selected;
+ case MAIN_HAND -> player.getInventory().getSelectedSlot();
case OFF_HAND -> Inventory.SLOT_OFFHAND;
};
}
/**
- * Map a slot inside a player's compartment to a slot in the full player's inventory.
- *
- * {@link Inventory#tick()} passes in a slot to {@link Item#inventoryTick(ItemStack, Level, Entity, int, boolean)}.
- * However, this slot corresponds to the index within the current compartment (items, armour, offhand) and not
- * the actual slot.
- *
- * This method searches the relevant compartments (inventory and offhand, skipping armour) for the stack, returning
- * its slot if found.
+ * Find an item inside a container.
*
- * @param player The player holding the item.
- * @param slot The slot inside the compartment.
- * @param stack The stack being ticked.
- * @return The inventory slot, or {@code -1} if the item could not be found in the inventory.
+ * @param container The container to search in.
+ * @param stack The item to find.
+ * @return The container slot, or {@code -1} if the item could not be found in the container.
*/
- public static int getInventorySlotFromCompartment(Player player, int slot, ItemStack stack) {
- if (stack.isEmpty()) throw new IllegalArgumentException("Cannot search for empty stack");
- if (player.getInventory().getItem(slot) == stack) return slot;
- if (player.getInventory().getItem(Inventory.SLOT_OFFHAND) == stack) return Inventory.SLOT_OFFHAND;
+ public static int findItemInInventory(Container container, ItemStack stack) {
+ for (int i = 0, size = container.getContainerSize(); i < size; i++) {
+ if (container.getItem(i) == stack) return i;
+ }
return -1;
}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/NBTUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/NBTUtil.java
index b4f65ceef..911079319 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/NBTUtil.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/NBTUtil.java
@@ -31,32 +31,26 @@ public final class NBTUtil {
}
public static @Nullable T decodeFrom(Codec codec, HolderLookup.Provider registries, CompoundTag tag, String key) {
- var childTag = tag.get(key);
- return childTag == null ? null : codec.parse(registries.createSerializationContext(NbtOps.INSTANCE), childTag)
- .resultOrPartial(e -> LOG.warn("Failed to parse NBT: {}", e))
- .orElse(null);
+ return tag.read(key, codec, registries.createSerializationContext(NbtOps.INSTANCE)).orElse(null);
}
public static void encodeTo(Codec codec, HolderLookup.Provider registries, CompoundTag destination, String key, @Nullable T value) {
- if (value == null) return;
- codec.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), value)
- .resultOrPartial(e -> LOG.warn("Failed to save NBT: {}", e))
- .ifPresent(x -> destination.put(key, x));
+ destination.storeNullable(key, codec, registries.createSerializationContext(NbtOps.INSTANCE), value);
}
public static @Nullable Object toLua(@Nullable Tag tag) {
if (tag == null) return null;
return switch (tag.getId()) {
- case Tag.TAG_BYTE, Tag.TAG_SHORT, Tag.TAG_INT, Tag.TAG_LONG -> ((NumericTag) tag).getAsLong();
- case Tag.TAG_FLOAT, Tag.TAG_DOUBLE -> ((NumericTag) tag).getAsDouble();
- case Tag.TAG_STRING -> tag.getAsString();
+ case Tag.TAG_BYTE, Tag.TAG_SHORT, Tag.TAG_INT, Tag.TAG_LONG -> ((NumericTag) tag).longValue();
+ case Tag.TAG_FLOAT, Tag.TAG_DOUBLE -> ((NumericTag) tag).doubleValue();
+ case Tag.TAG_STRING -> ((StringTag) tag).value();
case Tag.TAG_COMPOUND -> {
var compound = (CompoundTag) tag;
Map map = new HashMap<>(compound.size());
- for (var key : compound.getAllKeys()) {
- var value = toLua(compound.get(key));
- if (value != null) map.put(key, value);
+ for (var entry : compound.entrySet()) {
+ var value = toLua(entry.getValue());
+ if (value != null) map.put(entry.getKey(), value);
}
yield map;
}
@@ -110,7 +104,7 @@ public final class NBTUtil {
private static void writeTag(DataOutput output, Tag tag) throws IOException {
if (tag instanceof CompoundTag compound) {
- var keys = compound.getAllKeys().toArray(new String[0]);
+ var keys = compound.keySet().toArray(new String[0]);
Arrays.sort(keys);
for (var key : keys) writeNamedTag(output, key, Nullability.assertNonNull(compound.get(key)));
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/NonNegativeId.java b/projects/common/src/main/java/dan200/computercraft/shared/util/NonNegativeId.java
index db8d19746..a550b34d0 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/util/NonNegativeId.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/util/NonNegativeId.java
@@ -8,51 +8,101 @@ import com.mojang.serialization.Codec;
import dan200.computercraft.api.ComputerCraftAPI;
import io.netty.buffer.ByteBuf;
import net.minecraft.ChatFormatting;
+import net.minecraft.core.component.DataComponentGetter;
import net.minecraft.core.component.DataComponentType;
+import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ExtraCodecs;
+import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.TooltipFlag;
+import net.minecraft.world.item.component.TooltipProvider;
import org.jspecify.annotations.Nullable;
import java.util.function.Consumer;
+import java.util.function.IntFunction;
/**
* A non-negative integer id, used for computer and disk ids.
*
- * @param id The id of this entity.
* @see dan200.computercraft.shared.ModRegistry.DataComponents#COMPUTER_ID
* @see dan200.computercraft.shared.ModRegistry.DataComponents#DISK_ID
*/
-public record NonNegativeId(int id) {
- public static final Codec CODEC = ExtraCodecs.NON_NEGATIVE_INT.xmap(NonNegativeId::new, NonNegativeId::id);
+public abstract class NonNegativeId implements TooltipProvider {
+ private final int id;
- public static final StreamCodec STREAM_CODEC = ByteBufCodecs.VAR_INT.map(NonNegativeId::new, NonNegativeId::id);
-
- public NonNegativeId {
+ protected NonNegativeId(int id) {
if (id < 0) throw new IllegalArgumentException("ID must be >= 0");
+ this.id = id;
+ }
+
+ /**
+ * Get the internal id.
+ *
+ * @return The internal id.
+ */
+ public int id() {
+ return id;
}
public static int getId(@Nullable NonNegativeId id) {
return id == null ? -1 : id.id();
}
- public static @Nullable NonNegativeId of(int id) {
- return id >= 0 ? new NonNegativeId(id) : null;
- }
-
- public static int getOrCreate(MinecraftServer server, ItemStack stack, DataComponentType component, String type) {
+ public static int getOrCreate(MinecraftServer server, ItemStack stack, DataComponentType component, IntFunction create, String type) {
var id = stack.get(component);
if (id != null) return id.id();
- var diskID = ComputerCraftAPI.createUniqueNumberedSaveDir(server, type);
- stack.set(component, new NonNegativeId(diskID));
- return diskID;
+ var newId = ComputerCraftAPI.createUniqueNumberedSaveDir(server, type);
+ stack.set(component, create.apply(newId));
+ return newId;
}
- public void addToTooltip(String translation, Consumer out) {
+ protected void addToTooltip(String translation, Consumer out) {
out.accept(Component.translatable(translation, id()).withStyle(ChatFormatting.GRAY));
}
+
+ @Override
+ @SuppressWarnings("EqualsGetClass") // We want to distinguish different subclasses.
+ public final boolean equals(Object o) {
+ return this == o || (o != null && getClass() == o.getClass() && id == ((NonNegativeId) o).id);
+ }
+
+ @Override
+ public final int hashCode() {
+ return id;
+ }
+
+ public static final class Computer extends NonNegativeId {
+ public static final Codec CODEC = ExtraCodecs.NON_NEGATIVE_INT.xmap(Computer::new, NonNegativeId::id);
+ public static final StreamCodec STREAM_CODEC = ByteBufCodecs.VAR_INT.map(Computer::new, NonNegativeId::id);
+
+ public Computer(int id) {
+ super(id);
+ }
+
+ @Override
+ public void addToTooltip(Item.TooltipContext tooltipContext, Consumer out, TooltipFlag flags, DataComponentGetter stack) {
+ if (flags.isAdvanced() || stack.get(DataComponents.CUSTOM_NAME) == null) {
+ addToTooltip("gui.computercraft.tooltip.computer_id", out);
+ }
+ }
+ }
+
+ public static final class Disk extends NonNegativeId {
+ public static final Codec CODEC = ExtraCodecs.NON_NEGATIVE_INT.xmap(Disk::new, NonNegativeId::id);
+ public static final StreamCodec