diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index c246f3e56..5582c2f58 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -276,7 +276,6 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei } public static final class Properties { - private final int computerID; private @Nullable String label; private final ComputerFamily family; 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 936a753cd..ae4a434b0 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 @@ -18,6 +18,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -237,7 +238,7 @@ public class MonitorBlockEntity extends BlockEntity { getLevel().setBlock(getBlockPos(), getBlockState() .setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( yIndex < height - 1, yIndex > 0, - xIndex > 0, xIndex < width - 1)), 2); + xIndex > 0, xIndex < width - 1)), Block.UPDATE_CLIENTS); } // region Sizing and placement stuff 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 87090e614..4c62cb236 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 @@ -36,6 +36,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.MoverType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -280,7 +281,7 @@ public class TurtleBrain implements TurtleAccessInternal { try { // We use Block.UPDATE_CLIENTS here to ensure that neighbour updates caused in Block.updateNeighbourShapes // are sent to the client. We want to avoid doing a full block update until the turtle state is copied over. - if (world.setBlock(pos, newState, 2)) { + if (world.setBlock(pos, newState, Block.UPDATE_CLIENTS)) { var block = world.getBlockState(pos).getBlock(); if (block == oldBlock.getBlock()) { var newTile = world.getBlockEntity(pos); @@ -691,7 +692,7 @@ public class TurtleBrain implements TurtleAccessInternal { } var aabb = new AABB(minX, minY, minZ, maxX, maxY, maxZ); - var list = world.getEntitiesOfClass(Entity.class, aabb, TurtleBrain::canPush); + var list = world.getEntities((Entity) null, aabb, TurtleBrain::canPush); if (!list.isEmpty()) { double pushStep = 1.0f / ANIM_DURATION; var pushStepX = moveDir.getStepX() * pushStep; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java index 480ad209e..2ce7fb27c 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java @@ -22,11 +22,10 @@ public class TurtleDetectCommand implements TurtleCommand { var direction = this.direction.toWorldDir(turtle); // Check if thing in front is air or not - var world = turtle.getLevel(); - var oldPosition = turtle.getPosition(); - var newPosition = oldPosition.relative(direction); + var level = turtle.getLevel(); + var pos = turtle.getPosition().relative(direction); - return !WorldUtil.isLiquidBlock(world, newPosition) && !world.isEmptyBlock(newPosition) + return !WorldUtil.isEmptyBlock(level.getBlockState(pos)) ? TurtleCommandResult.success() : TurtleCommandResult.failure(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java index 02778bb93..c6990fa4b 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java @@ -13,9 +13,9 @@ import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; public class TurtleMoveCommand implements TurtleCommand { private final MoveDirection direction; @@ -30,57 +30,32 @@ public class TurtleMoveCommand implements TurtleCommand { var direction = this.direction.toWorldDir(turtle); // Check if we can move - var oldWorld = (ServerLevel) turtle.getLevel(); + var level = (ServerLevel) turtle.getLevel(); var oldPosition = turtle.getPosition(); var newPosition = oldPosition.relative(direction); var turtlePlayer = TurtlePlayer.getWithPosition(turtle, oldPosition, direction); - var canEnterResult = canEnter(turtlePlayer, oldWorld, newPosition); - if (!canEnterResult.isSuccess()) { - return canEnterResult; - } + var canEnterResult = canEnter(turtlePlayer, level, newPosition); + if (!canEnterResult.isSuccess()) return canEnterResult; - // Check existing block is air or replaceable - var state = oldWorld.getBlockState(newPosition); - if (!oldWorld.isEmptyBlock(newPosition) && - !WorldUtil.isLiquidBlock(oldWorld, newPosition) && - !state.canBeReplaced()) { + // Check existing block is air or replaceable. + var existingState = level.getBlockState(newPosition); + if (!(WorldUtil.isEmptyBlock(existingState) || existingState.canBeReplaced())) { return TurtleCommandResult.failure("Movement obstructed"); } - // Check there isn't anything in the way - var collision = state.getCollisionShape(oldWorld, oldPosition).move( - newPosition.getX(), - newPosition.getY(), - newPosition.getZ() - ); - - if (!oldWorld.isUnobstructed(null, collision)) { - if (!Config.turtlesCanPush || this.direction == MoveDirection.UP || this.direction == MoveDirection.DOWN) { - return TurtleCommandResult.failure("Movement obstructed"); - } - - // Check there is space for all the pushable entities to be pushed - var list = oldWorld.getEntitiesOfClass(Entity.class, getBox(collision), x -> x != null && x.isAlive() && x.blocksBuilding); - for (var entity : list) { - var pushedBB = entity.getBoundingBox().move( - direction.getStepX(), - direction.getStepY(), - direction.getStepZ() - ); - if (!oldWorld.isUnobstructed(null, Shapes.create(pushedBB))) { - return TurtleCommandResult.failure("Movement obstructed"); - } - } + // Check there isn't an entity in the way. + var turtleShape = level.getBlockState(oldPosition).getCollisionShape(level, oldPosition) + .move(newPosition.getX(), newPosition.getY(), newPosition.getZ()); + if (!level.isUnobstructed(null, turtleShape) && !canPushEntities(level, turtleShape.bounds())) { + return TurtleCommandResult.failure("Movement obstructed"); } // Check fuel level - if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) { - return TurtleCommandResult.failure("Out of fuel"); - } + if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) return TurtleCommandResult.failure("Out of fuel"); // Move - if (!turtle.teleportTo(oldWorld, newPosition)) return TurtleCommandResult.failure("Movement failed"); + if (!turtle.teleportTo(level, newPosition)) return TurtleCommandResult.failure("Movement failed"); // Consume fuel turtle.consumeFuel(1); @@ -114,9 +89,20 @@ public class TurtleMoveCommand implements TurtleCommand { return TurtleCommandResult.success(); } - private static AABB getBox(VoxelShape shape) { - return shape.isEmpty() ? EMPTY_BOX : shape.bounds(); - } - private static final AABB EMPTY_BOX = new AABB(0, 0, 0, 0, 0, 0); + /** + * Determine if all entities in the given bounds can be pushed by the turtle. + * + * @param level The current level. + * @param bounds The bounding box. + * @return Whether all entities can be pushed. + */ + private boolean canPushEntities(Level level, AABB bounds) { + if (!Config.turtlesCanPush) return false; + + // Check there is space for all the pushable entities to be pushed + return level.getEntities((Entity) null, bounds, e -> e.isAlive() + && !e.isSpectator() && e.blocksBuilding && e.getPistonPushReaction() == PushReaction.IGNORE + ).isEmpty(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java index 049e4bf37..046967211 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java @@ -29,9 +29,14 @@ import net.minecraft.world.phys.shapes.VoxelShape; import javax.annotation.Nullable; public final class WorldUtil { + @SuppressWarnings("deprecation") public static boolean isLiquidBlock(Level world, BlockPos pos) { - if (!world.isInWorldBounds(pos)) return false; - return world.getBlockState(pos).liquid(); + return world.isInWorldBounds(pos) && world.getBlockState(pos).liquid(); + } + + @SuppressWarnings("deprecation") + public static boolean isEmptyBlock(BlockState state) { + return state.isAir() || state.liquid(); } public static boolean isVecInside(VoxelShape shape, Vec3 vec) { diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt index 69b113f0b..5310f52cd 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt @@ -642,6 +642,27 @@ class Turtle_Test { } } + /** + * Test turtles can push entities. + */ + @GameTest + fun Move_push_entity(helper: GameTestHelper) = helper.sequence { + thenOnComputer { turtle.up().await().assertArrayEquals(true) } + thenIdle(9) + thenExecute { + // The turtle has moved up + helper.assertBlockPresent(ModRegistry.Blocks.TURTLE_NORMAL.get(), BlockPos(2, 3, 2)) + + // As has the villager + val pos = BlockPos(2, 4, 2) + helper.assertEntityPresent(EntityType.VILLAGER, pos) + + val villager = helper.getEntity(EntityType.VILLAGER) + val expectedY = helper.absolutePos(pos).y - 0.125 + if (villager.y < expectedY) helper.fail("Expected villager at y>=$expectedY, but at ${villager.y}", pos) + } + } + /** * Test a turtle can attack an entity and capture its drops. */ diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt index 05a06ee4f..6804668b3 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt @@ -36,6 +36,10 @@ object ManagedComputers : ILuaMachine.Factory { private val LOGGER = LoggerFactory.getLogger(ManagedComputers::class.java) private val computers: MutableMap Unit>> = mutableMapOf() + internal fun reset() { + computers.clear() + } + internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor { val monitor = Monitor(test, label) computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add { diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt index b1510719e..d18cda24a 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt @@ -73,6 +73,8 @@ object TestHooks { LOG.info("Cleaning up after last run") GameTestRunner.clearAllTests(server.overworld(), BlockPos(0, -60, 0), GameTestTicker.SINGLETON, 200) + ManagedComputers.reset() + // Delete server context and add one with a mutable machine factory. This allows us to set the factory for // specific test batches without having to reset all computers. for (computer in ServerContext.get(server).registry().computers) { diff --git a/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt new file mode 100644 index 000000000..2c043fe6d --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt @@ -0,0 +1,140 @@ +{ + DataVersion: 3465, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {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: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {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: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:white_stained_glass"}, + {pos: [1, 1, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 1, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 1, Label: "turtle_test.move_push_entity", Fuel: 80, Items: [], On: 1b, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 1, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 1, 3], state: "minecraft:white_stained_glass"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:white_stained_glass"}, + {pos: [1, 2, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 2, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 2, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 2, 3], state: "minecraft:white_stained_glass"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:white_stained_glass"}, + {pos: [1, 3, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 3, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 3, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 3, 3], state: "minecraft:white_stained_glass"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:white_stained_glass"}, + {pos: [1, 4, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 4, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 4, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 4, 3], state: "minecraft:white_stained_glass"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [ + {blockPos: [2, 1, 2], pos: [2.5d, 1.875d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Age: 0, Air: 300s, ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.5d, Name: "minecraft:generic.movement_speed"}, {Base: 48.0d, Modifiers: [{Amount: -0.01165046535152748d, Name: "Random spawn bonus", Operation: 1, UUID: [I; 1412502412, 1522745411, -1211155694, 2103054347]}], Name: "minecraft:generic.follow_range"}], Brain: {memories: {}}, CanPickUpLoot: 1b, DeathTime: 0s, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, FoodLevel: 0b, ForcedAge: 0, Gossips: [], HandDropChances: [0.085f, 0.085f], HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Inventory: [], Invulnerable: 0b, LastGossipDecay: 52357L, LastRestock: 0L, LeftHanded: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], OnGround: 1b, PersistenceRequired: 0b, PortalCooldown: 0, Pos: [-33.5d, 58.875d, -21.5d], RestocksToday: 0, Rotation: [-102.704926f, 0.0f], UUID: [I; 164071932, -867285780, -1817215456, -2129864016], VillagerData: {level: 1, profession: "minecraft:none", type: "minecraft:desert"}, Xp: 0, id: "minecraft:villager"}} + ], + palette: [ + "minecraft:polished_andesite", + "minecraft:white_stained_glass", + "minecraft:air", + "computercraft:turtle_normal{facing:west,waterlogged:false}" + ] +} diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 11690ae41..700f8a777 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -408,8 +408,9 @@ public class PlatformHelperImpl implements PlatformHelper { } } - private record WrappedMenuProvider(Component title, MenuConstructor menu, - ContainerData data) implements ExtendedScreenHandlerFactory { + private record WrappedMenuProvider( + Component title, MenuConstructor menu, ContainerData data + ) implements ExtendedScreenHandlerFactory { @Nullable @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {