mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-04-18 16:53:18 +00:00
Redo turtle move checks
Oh, this is so broken, and really has been since the 1.13 update, if not earlier. - Fix call to isUnobstructed using the bounding box of the *destination* block rather than the turtle. This is almost always air, so the box is empty. - Because the above check has been wrong for so many years, we now significantly relax the "can push" checks for entities. We now allow pushing entities in any direction. We also remove the "isUnobstructed" check for the destination entity pos. This causes problems (if two entities are standing on a turtle, they'll obstruct each other), and given pistons don't perform such a check, I don't think we need it. - Also do a bit of cleanup around air/liquid checks. We often ended up reading the block state multiple times, which is a little ugly.
This commit is contained in:
parent
7d8f609c49
commit
b69a44a927
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -36,6 +36,10 @@ object ManagedComputers : ILuaMachine.Factory {
|
||||
private val LOGGER = LoggerFactory.getLogger(ManagedComputers::class.java)
|
||||
private val computers: MutableMap<String, Queue<suspend LuaTaskContext.() -> 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 {
|
||||
|
@ -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) {
|
||||
|
140
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt
generated
Normal file
140
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt
generated
Normal file
@ -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}"
|
||||
]
|
||||
}
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user