Handle upgrade items in data fixers

This is not perfect, but I think as good as we're going to get it.
Because ItemStack's data is not typed in older versions, we don't apply
data fixers to upgrade items (aside from the componentisation fix). This
means that any migration between 3564 and 3818.5 will be skipped.
This commit is contained in:
Jonathan Coates committed 2026-10-22 20:11:29 +01:00
1 parent 8acc600e42
commit 4f07d2d024
12 files changed
+149 -55

No files matched your search

@@ -143,7 +143,7 @@ public final class UpgradeData<T extends UpgradeBase> {
if (obj == this) return true;
return obj instanceof UpgradeData<?> that
&& upgrade == that.upgrade && data.equals(that.data)
&& ((stack == null && that.stack == null) || ItemStack.isSameItemSameComponents(getStack(), that.getStack()));
&& ((stack == null && that.stack == null) || ItemStack.matches(getStack(), that.getStack()));
}
@Override
@@ -153,6 +153,6 @@ public final class UpgradeData<T extends UpgradeBase> {
@Override
public String toString() {
return "UpgradeData[upgrade=" + upgrade.key().location() + ", data=" + data + ", stack=" + stack + "]";
return "UpgradeData[upgrade=" + upgrade.key().location() + ", data=" + data + ", stack=" + (stack == null ? "null" : stack.getItem().toString() + stack.getComponentsPatch()) + "]";
}
}
@@ -5,9 +5,9 @@
package dan200.computercraft.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.datafixers.DataFixUtils;
import com.mojang.datafixers.DataFixerBuilder;
import com.mojang.datafixers.schemas.Schema;
import dan200.computercraft.shared.datafix.ComponentizationFixers;
import dan200.computercraft.shared.datafix.TurtleUpgradeComponentizationFix;
import net.minecraft.util.datafix.DataFixers;
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
@@ -37,7 +37,7 @@ abstract class DataFixersMixin {
)
@SuppressWarnings("UnusedMethod")
private static Schema addComponentizationFixes(Schema schema, @Local DataFixerBuilder builder) {
assertSchemaVersion(schema, DataFixUtils.makeKey(3818, 5));
assertSchemaVersion(schema, ComponentizationFixers.SCHEMA_VERSION);
builder.addFixer(new TurtleUpgradeComponentizationFix(schema));
return schema;
}
@@ -7,6 +7,7 @@ package dan200.computercraft.mixin;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.datafixers.types.templates.TypeTemplate;
import dan200.computercraft.shared.datafix.ComponentizationFixers;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
@@ -24,7 +25,7 @@ import java.util.Map;
import java.util.function.Supplier;
/**
* Register our item-contianing block entities.
* Register our item-containing block entities.
*
* @see DiskDriveBlockEntity
* @see PrinterBlockEntity
@@ -48,24 +49,27 @@ abstract class V1460Mixin {
));
}
private static TypeTemplate upgradeData(Schema schema) {
return DSL.or(
// Pre-1.20.5 we just use the upgrade ID.
DSL.constType(NamespacedSchema.namespacedString()),
// In newer versions this is represented as a component.
DSL.optionalFields(
"item", References.ITEM_STACK.in(schema),
"components", References.DATA_COMPONENTS.in(schema)
)
);
private static TypeTemplate upgradeData(Schema schema, ComponentizationFixers.UpgradeKeys keys) {
if (schema.getVersionKey() >= ComponentizationFixers.SCHEMA_VERSION) {
// In newer versions this is represented in one field.
return DSL.optional(DSL.field(keys.id(), ComponentizationFixers.upgradeDataType(schema)));
} else {
// Pre-1.20.5 we store the upgrade ID and item separately
return DSL.and(
DSL.optional(DSL.field(keys.id(), DSL.constType(NamespacedSchema.namespacedString()))),
DSL.optional(DSL.field(keys.item(), References.ITEM_STACK.in(schema)))
);
}
}
@Unique
private static void registerTurtle(Schema schema, Map<String, Supplier<TypeTemplate>> map, String name) {
schema.register(map, name, () -> DSL.optionalFields(
"LeftUpgrade", upgradeData(schema),
"RightUpgrade", upgradeData(schema),
"Items", DSL.list(References.ITEM_STACK.in(schema))
schema.register(map, name, () -> DSL.and(
upgradeData(schema, ComponentizationFixers.TURTLE_UPGRADE_LEFT),
upgradeData(schema, ComponentizationFixers.TURTLE_UPGRADE_RIGHT),
DSL.optionalFields(
"Items", DSL.list(References.ITEM_STACK.in(schema))
)
));
}
@@ -5,6 +5,7 @@
package dan200.computercraft.shared.datafix;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixUtils;
import com.mojang.datafixers.Typed;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.datafixers.types.templates.TypeTemplate;
@@ -17,6 +18,8 @@ import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.media.items.PrintoutData;
import net.minecraft.util.datafix.fixes.ItemStackComponentizationFix;
import net.minecraft.util.datafix.fixes.References;
import net.minecraft.util.datafix.schemas.NamespacedSchema;
import net.minecraft.util.datafix.schemas.V3818_5;
import java.util.ArrayList;
import java.util.Arrays;
@@ -26,12 +29,19 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraft.Util.writeAndReadTypedOrThrow;
/**
* Additional fixers to convert CC's item NBT to use components.
*
* @see ItemStackComponentizationFix
*/
public class ComponentizationFixers {
/**
* The version of the {@link V3818_5} schema where we apply our changes.
*/
public static final int SCHEMA_VERSION = DataFixUtils.makeKey(3818, 5);
private static final String TURTLE_NORMAL = "computercraft:turtle_normal";
private static final String TURTLE_ADVANCED = "computercraft:turtle_advanced";
@@ -51,6 +61,16 @@ public class ComponentizationFixers {
Stream.of(DISK)
).collect(Collectors.toUnmodifiableSet());
public static final UpgradeKeys POCKET_UPGRADE = new UpgradeKeys("Upgrade", "UpgradeInfo");
public static final UpgradeKeys TURTLE_UPGRADE_LEFT = new UpgradeKeys("LeftUpgrade", "LeftUpgradeNbt");
public static final UpgradeKeys TURTLE_UPGRADE_RIGHT = new UpgradeKeys("RightUpgrade", "RightUpgradeNbt");
public record UpgradeKeys(String id, String item, String data) {
private UpgradeKeys(String id, String data) {
this(id, id + "Item", data);
}
}
/**
* Fix components in item stacks.
*
@@ -67,7 +87,7 @@ public class ComponentizationFixers {
if (item.is(POCKET_COMPUTERS)) {
item.moveTagToComponent("On", "computercraft:on");
moveUpgradeToComponent(item, ops, "Upgrade", "UpgradeInfo", "computercraft:pocket_upgrade");
moveUpgradeToComponent(item, ops, POCKET_UPGRADE, "computercraft:pocket_upgrade");
// Remove instance/session, so they don't end up in minecraft:custom_data.
item.removeTag("InstanceId");
@@ -79,8 +99,8 @@ public class ComponentizationFixers {
item.removeTag("Overlay").result().ifPresent(x -> item.setComponent("computercraft:overlay", fixOverlay(x)));
moveUpgradeToComponent(item, ops, "LeftUpgrade", "LeftUpgradeNbt", "computercraft:left_turtle_upgrade");
moveUpgradeToComponent(item, ops, "RightUpgrade", "RightUpgradeNbt", "computercraft:right_turtle_upgrade");
moveUpgradeToComponent(item, ops, TURTLE_UPGRADE_LEFT, "computercraft:left_turtle_upgrade");
moveUpgradeToComponent(item, ops, TURTLE_UPGRADE_RIGHT, "computercraft:right_turtle_upgrade");
}
if (item.is(DISK)) item.moveTagToComponent("DiskId", "computercraft:disk_id");
@@ -100,11 +120,11 @@ public class ComponentizationFixers {
if (item.is(PRINTOUTS)) movePrintoutToComponent(item, ops);
}
private static void moveUpgradeToComponent(ItemStackComponentizationFix.ItemStackData data, Dynamic<?> ops, String key, String dataKey, String component) {
// Rewrite {Upgrade:"foo",UpgradeData:...} to {cc:upgrade:{id:"foo", components:...}}
var upgrade = data.removeTag(key).asString(null);
private static void moveUpgradeToComponent(ItemStackComponentizationFix.ItemStackData data, Dynamic<?> ops, UpgradeKeys keys, String component) {
// Rewrite {Upgrade:"foo",UpgradeItem:...UpgradeData:...} to {cc:upgrade:{id:"foo", "item": ..., components:...}}
var upgrade = data.removeTag(keys.id()).asString(null);
if (upgrade == null) return;
data.setComponent(component, createUpgradeData(ops, upgrade, data.removeTag(dataKey)));
data.setComponent(component, createUpgradeData(ops, upgrade, data.removeTag(keys.item()), true, data.removeTag(keys.data())));
}
private static void moveColourToComponent(ItemStackComponentizationFix.ItemStackData item, Dynamic<?> ops, String key) {
@@ -154,20 +174,20 @@ public class ComponentizationFixers {
var input = DSL.namedChoice(name, inputSchema.getChoiceType(References.BLOCK_ENTITY, name));
var output = outputSchema.getChoiceType(References.BLOCK_ENTITY, name);
return typed -> typed.updateTyped(input, output, typed2 -> typed2.update(DSL.remainderFinder(), x -> {
x = moveUpgradeData(x, "LeftUpgrade", "LeftUpgradeNbt");
x = moveUpgradeData(x, "RightUpgrade", "RightUpgradeNbt");
return beData -> beData.updateTyped(input, output, turtleData -> writeAndReadTypedOrThrow(turtleData, output, x -> {
x = moveUpgradeData(x, TURTLE_UPGRADE_LEFT);
x = moveUpgradeData(x, TURTLE_UPGRADE_RIGHT);
x = x.update("Overlay", ComponentizationFixers::fixOverlay);
return x;
}));
}
private static Dynamic<?> moveUpgradeData(Dynamic<?> ops, String key, String dataKey) {
// Rewrite {Upgrade:"foo",UpgradeData:...} to {Upgrade:{id:"foo", components:...}}
var upgradeId = ops.get(key).asString(null);
private static Dynamic<?> moveUpgradeData(Dynamic<?> ops, UpgradeKeys keys) {
// Rewrite {Upgrade:"foo",UpgradeItem:...,UpgradeData:...} to {Upgrade:{id:"foo", item: ..., components:...}}
var upgradeId = ops.get(keys.id()).asString(null);
if (upgradeId == null) return ops;
return ops.set(key, createUpgradeData(ops, upgradeId, ops.get(dataKey))).remove(dataKey);
return ops.set(keys.id(), createUpgradeData(ops, upgradeId, ops.get(keys.item()), false, ops.get(keys.data()))).remove(keys.item()).remove(keys.data());
}
private static Dynamic<?> fixOverlay(Dynamic<?> overlay) {
@@ -189,12 +209,23 @@ public class ComponentizationFixers {
*
* @param ops A {@link Dynamic} instance, for creating new dynamic values.
* @param upgradeId The upgrade ID
* @param upgradeItem The item for this upgrade.
* @param convertItem Whether to also apply item-stack fixes to the upgrade item. When converting an upgrade on
* an item, DFUs are *not* applied to the nested item (as the ItemStack info does not contain
* sufficient information). However, they *are* applied when converting an upgrade on a BE, so we
* need to avoid the double-conversion.
* @param upgradeData Additional upgrade data
* @return The newly created {@link UpgradeData}-compatible value.
*/
private static Dynamic<?> createUpgradeData(Dynamic<?> ops, String upgradeId, OptionalDynamic<?> upgradeData) {
private static Dynamic<?> createUpgradeData(Dynamic<?> ops, String upgradeId, OptionalDynamic<?> upgradeItem, boolean convertItem, OptionalDynamic<?> upgradeData) {
var newUpgrade = ops.emptyMap().set("id", ops.createString(upgradeId));
// Migrate upgrade item
var item = upgradeItem.result().orElse(null);
if (item != null) {
newUpgrade = newUpgrade.set("item", convertItem ? fixItem(item) : item);
}
var data = upgradeData.result().orElse(null);
if (data != null && !data.equals(ops.emptyMap())) {
// Migrate all existing data to minecraft:custom_data
@@ -204,6 +235,15 @@ public class ComponentizationFixers {
return newUpgrade;
}
private static Dynamic<?> fixItem(Dynamic<?> dynamic) {
var parsedData = ItemStackComponentizationFix.ItemStackData.read(dynamic);
if (parsedData.isEmpty()) return dynamic;
var data = parsedData.get();
ItemStackComponentizationFix.fixItemStack(data, data.tag);
return data.write();
}
/**
* Add our custom data components to the datafixer system.
*
@@ -217,8 +257,7 @@ public class ComponentizationFixers {
*/
public static TypeTemplate addExtraTypes(TypeTemplate type, Schema schema) {
// Create a codec for UpgradeData
var upgradeData = DSL.optionalFields("components", References.DATA_COMPONENTS.in(schema));
var upgradeData = upgradeDataType(schema);
return extraOptionalFields(type,
Pair.of("computercraft:pocket_upgrade", upgradeData),
Pair.of("computercraft:left_turtle_upgrade", upgradeData),
@@ -226,6 +265,20 @@ public class ComponentizationFixers {
);
}
/**
* Get the {@link TypeTemplate} for our {@link UpgradeData}.
*
* @param schema The current schema
* @return The upgrade data.
*/
public static TypeTemplate upgradeDataType(Schema schema) {
return DSL.optionalFields(
"id", DSL.constType(NamespacedSchema.namespacedString()),
"item", References.ITEM_STACK.in(schema),
"components", References.DATA_COMPONENTS.in(schema)
);
}
@SafeVarargs
@SuppressWarnings("varargs")
private static TypeTemplate extraOptionalFields(TypeTemplate base, Pair<String, TypeTemplate>... fields) {
@@ -33,4 +33,7 @@ accessible field net/minecraft/client/sounds/SoundEngine executor Lnet/minecraft
# Turtle model
accessible method net/minecraft/client/renderer/block/model/ItemOverrides <init> ()V
# Data fixers
accessible method net/minecraft/util/datafix/fixes/ItemStackComponentizationFix fixItemStack (Lnet/minecraft/util/datafix/fixes/ItemStackComponentizationFix$ItemStackData;Lcom/mojang/serialization/Dynamic;)V
accessible class net/minecraft/util/datafix/fixes/ItemStackComponentizationFix$ItemStackData
accessible field net/minecraft/util/datafix/fixes/ItemStackComponentizationFix$ItemStackData tag Lcom/mojang/serialization/Dynamic;
@@ -24,6 +24,7 @@ import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.gametest.framework.GameTestSequence
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import org.junit.jupiter.api.Assertions.assertEquals
@@ -119,9 +120,13 @@ class Pocket_Computer_Test {
@GameTest
fun Data_fixers(helper: GameTestHelper) = helper.sequence {
thenExecute {
val upgrade = helper.level.registryAccess().registryOrThrow(IPocketUpgrade.REGISTRY)
.getHolder(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "wireless_modem_normal"))
.orElseThrow()
val upgrade = UpgradeData.of(
helper.level.registryAccess().getOrThrow(
ResourceKey.create(IPocketUpgrade.REGISTRY, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "wireless_modem_advanced")),
),
DataComponentUtil.createStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get(), DataComponents.CUSTOM_NAME, Component.literal("My favourite modem")),
DataComponentPatch.EMPTY,
)
helper.assertContainerExactly(
BlockPos(2, 2, 2),
@@ -131,7 +136,8 @@ class Pocket_Computer_Test {
it.applyComponents(
DataComponentPatch.builder()
.set(ModRegistry.DataComponents.COMPUTER_ID.get(), NonNegativeId(123))
.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))
.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), upgrade)
.set(ModRegistry.DataComponents.ON.get(), false)
.build(),
)
},
@@ -67,6 +67,9 @@ class Recipe_Test {
val pocketUpgrades = registries.lookupOrThrow(IPocketUpgrade.REGISTRY)
val generator = UpgradeRecipeGenerator({ it }, registries)
println("Got packs => " + context.level.server.packRepository.selectedIds)
println("Got items => " + turtleUpgrades.listElementIds().toList())
fun checkUsage(message: String, size: Int, item: ItemStack) {
val recipes = generator.findRecipesWithInput(item)
assertEquals(size, recipes.size, message)
@@ -28,21 +28,26 @@ import dan200.computercraft.shared.turtle.TurtleOverlay
import dan200.computercraft.shared.turtle.apis.TurtleAPI
import dan200.computercraft.shared.turtle.core.TurtleCraftCommand
import dan200.computercraft.shared.turtle.items.TurtleItem
import dan200.computercraft.shared.util.DataComponentUtil
import dan200.computercraft.shared.util.WaterloggableHelpers
import dan200.computercraft.test.core.assertArrayEquals
import dan200.computercraft.test.core.computer.LuaTaskContext
import dan200.computercraft.test.core.computer.getApi
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.core.BlockPos
import net.minecraft.core.component.DataComponentPatch
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.Registries
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.item.PrimedTnt
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.component.ItemContainerContents
import net.minecraft.world.item.enchantment.Enchantments
import net.minecraft.world.level.block.BeehiveBlock
import net.minecraft.world.level.block.Blocks
@@ -324,8 +329,7 @@ class Turtle_Test {
val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access
val upgrade = turtle.getUpgrade(TurtleSide.LEFT)
assertEquals(
helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation.fromNamespaceAndPath("cctest", "wooden_pickaxe")),
helper.level.registryAccess().getOrThrow(WOODEN_PICKAXE).value(),
upgrade,
"Upgrade is a wooden pickaxe",
)
@@ -352,10 +356,7 @@ class Turtle_Test {
helper.assertUpgradeItem(
ItemStack(Items.WOODEN_PICKAXE),
UpgradeData.ofDefault(
helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.getHolder(ResourceLocation.fromNamespaceAndPath("cctest", "wooden_pickaxe")).orElseThrow(),
),
UpgradeData.ofDefault(helper.level.registryAccess().getOrThrow(WOODEN_PICKAXE)),
)
}
}
@@ -773,16 +774,27 @@ class Turtle_Test {
thenExecute {
val overlay = helper.level.registryAccess().registryOrThrow(TurtleOverlay.REGISTRY)
.get(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "trans_flag"))!!
val upgrade = helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation.withDefaultNamespace("diamond_pickaxe"))!!
val pickaxeUpgrade = UpgradeData.ofDefault(
helper.level.registryAccess().getOrThrow(id(ResourceLocation.withDefaultNamespace("diamond_pickaxe"))),
)
val shulkerUpgrade = UpgradeData.of(
helper.level.registryAccess()
.getOrThrow(id(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "shulker"))),
DataComponentUtil.createStack(Items.SHULKER_BOX, DataComponents.CONTAINER, ItemContainerContents.fromItems(listOf(ItemStack(Items.DIRT)))),
DataComponentPatch.EMPTY,
)
val turtleBe = helper.getBlockEntity(BlockPos(1, 2, 1), ModRegistry.BlockEntities.TURTLE_NORMAL.get())
assertEquals(overlay, turtleBe.overlay)
assertEquals(upgrade, turtleBe.getUpgrade(TurtleSide.LEFT))
assertEquals(pickaxeUpgrade, turtleBe.access.getUpgradeWithData(TurtleSide.LEFT))
assertEquals(shulkerUpgrade, turtleBe.access.getUpgradeWithData(TurtleSide.RIGHT))
val turtleItem = turtleBe.getItem(0)
assertEquals(overlay, TurtleItem.getOverlay(turtleItem))
assertEquals(upgrade, TurtleItem.getUpgrade(turtleItem, TurtleSide.LEFT))
assertEquals(pickaxeUpgrade, TurtleItem.getUpgradeWithData(turtleItem, TurtleSide.LEFT))
assertEquals(shulkerUpgrade, TurtleItem.getUpgradeWithData(turtleItem, TurtleSide.RIGHT))
}
}
@@ -1070,6 +1082,13 @@ class Turtle_Test {
thenExecute { helper.positionAtArmorStand() }
thenScreenshot()
}
companion object {
private fun id(key: ResourceLocation): ResourceKey<ITurtleUpgrade> =
ResourceKey.create(ITurtleUpgrade.REGISTRY, key)
private val WOODEN_PICKAXE = id(ResourceLocation.fromNamespaceAndPath("cctest", "wooden_pickaxe"))
}
}
private val LuaTaskContext.turtle get() = getApi<TurtleAPI>()
@@ -13,11 +13,10 @@ import dan200.computercraft.shared.util.RegistryHelper
import dan200.computercraft.test.core.computer.LuaTaskContext
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.commands.arguments.blocks.BlockInput
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.NonNullList
import net.minecraft.core.*
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.gametest.framework.*
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.Container
import net.minecraft.world.InteractionHand
@@ -423,3 +422,7 @@ inline fun tryMultipleTimes(count: Int, action: () -> Unit) {
}
}
}
/** Find an entry in a registry provider. */
fun <T> HolderLookup.Provider.getOrThrow(key: ResourceKey<T>): Holder.Reference<T> =
lookupOrThrow(key.registryKey()).getOrThrow(key)
@@ -27,7 +27,7 @@
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 1, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [{Count: 1b, Slot: 0b, id: "computercraft:pocket_computer_advanced", tag: {ComputerId: 123, Upgrade: "computercraft:wireless_modem_normal", display: {Name: '{"text":"Test"}'}}}], id: "minecraft:chest"}}
{pos: [2, 1, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [{Count: 1b, Slot: 0b, id: "computercraft:pocket_computer_advanced", tag: {ComputerId: 123, InstanceId: [I; -1496601984, 1527793399, -1542736776, 2137516638], On: 0b, SessionId: 1591798634, Upgrade: "computercraft:wireless_modem_advanced", UpgradeItem: {Count: 1b, id: "computercraft:wireless_modem_advanced", tag: {RepairCost: 0, display: {Name: '{"text":"My favourite modem"}'}}}, display: {Name: '{"text":"Test"}'}}}], id: "minecraft:chest"}}
],
entities: [],
palette: [
@@ -11,7 +11,7 @@
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 123, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {ComputerId: 123, LeftUpgrade: "minecraft:diamond_pickaxe", Overlay: "computercraft:block/turtle_trans_overlay"}}], LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeNbt: {}, On: 0b, Overlay: "computercraft:block/turtle_trans_overlay", Owner: {LowerId: -4770154215762454977L, Name: "Player436", UpperId: 114963728012426084L}, Slot: 0, id: "computercraft:turtle_normal"}},
{pos: [1, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 123, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {ComputerId: 123, LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeItem: {Count: 1b, id: "minecraft:diamond_pickaxe", tag: {Damage: 0}}, Overlay: "computercraft:block/turtle_trans_overlay", RightUpgrade: "computercraft:shulker", RightUpgradeItem: {Count: 1b, id: "minecraft:shulker_box", tag: {BlockEntityTag: {Items: [{Count: 1b, Slot: 0b, id: "minecraft:dirt"}], id: "minecraft:shulker_box"}}}}}], LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeItem: {Count: 1b, id: "minecraft:diamond_pickaxe", tag: {Damage: 0}}, On: 1b, Overlay: "computercraft:block/turtle_trans_overlay", Owner: {LowerId: -4770154215762454977L, Name: "Player436", UpperId: 114963728012426084L}, RightUpgrade: "computercraft:shulker", RightUpgradeItem: {Count: 1b, id: "minecraft:shulker_box", tag: {BlockEntityTag: {Items: [{Count: 1b, Slot: 0b, id: "minecraft:dirt"}], id: "minecraft:shulker_box"}}}, Slot: 0, id: "computercraft:turtle_normal"}}
],
entities: [],
palette: [
@@ -33,4 +33,7 @@ public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minec
public net.minecraft.data.models.ItemModelGenerators generateFlatItem(Lnet/minecraft/world/item/Item;Ljava/lang/String;Lnet/minecraft/data/models/model/ModelTemplate;)V
public net.minecraft.data.models.model.TextureSlot create(Ljava/lang/String;)Lnet/minecraft/data/models/model/TextureSlot;
# Data fixers
public net.minecraft.util.datafix.fixes.ItemStackComponentizationFix fixItemStack(Lnet/minecraft/util/datafix/fixes/ItemStackComponentizationFix$ItemStackData;Lcom/mojang/serialization/Dynamic;)V
public net.minecraft.util.datafix.fixes.ItemStackComponentizationFix$ItemStackData
public net.minecraft.util.datafix.fixes.ItemStackComponentizationFix$ItemStackData tag