diff --git a/patches/server/0735-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch b/patches/server/0735-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch index 4b957d3425..b8de158fc0 100644 --- a/patches/server/0735-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch +++ b/patches/server/0735-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch @@ -1214,6 +1214,231 @@ index 5f957a28e9d30144f724ebdc581d5f0b80bf6dc1..23ab7960120c1e2a76880f634787a089 entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); } +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 6068d70f5eb566396d8c6bf6137e2c6cf21e33cf..3f3be1147ef75918eeb3004fe191e40f59fec754 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -1176,10 +1176,45 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + this.tryCheckInsideBlocks(); + float f = this.getBlockSpeedFactor(); + +- this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f)); +- if (this.level().getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((iblockdata2) -> { +- return iblockdata2.is(BlockTags.FIRE) || iblockdata2.is(Blocks.LAVA); +- })) { ++ this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2)); ++ // Paper start - remove expensive streams from here ++ boolean noneMatch = true; ++ AABB fireSearchBox = this.getBoundingBox().deflate(1.0E-6D); ++ { ++ int minX = Mth.floor(fireSearchBox.minX); ++ int minY = Mth.floor(fireSearchBox.minY); ++ int minZ = Mth.floor(fireSearchBox.minZ); ++ int maxX = Mth.floor(fireSearchBox.maxX); ++ int maxY = Mth.floor(fireSearchBox.maxY); ++ int maxZ = Mth.floor(fireSearchBox.maxZ); ++ fire_search_loop: ++ for (int fz = minZ; fz <= maxZ; ++fz) { ++ for (int fx = minX; fx <= maxX; ++fx) { ++ for (int fy = minY; fy <= maxY; ++fy) { ++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)this.level.getChunkIfLoadedImmediately(fx >> 4, fz >> 4); ++ if (chunk == null) { ++ // Vanilla rets an empty stream if all the chunks are not loaded, so noneMatch will be true ++ // even if we're in lava/fire ++ noneMatch = true; ++ break fire_search_loop; ++ } ++ if (!noneMatch) { ++ // don't do get type, we already know we're in fire - we just need to check the chunks ++ // loaded state ++ continue; ++ } ++ ++ BlockState type = chunk.getBlockStateFinal(fx, fy, fz); ++ if (type.is(BlockTags.FIRE) || type.is(Blocks.LAVA)) { ++ noneMatch = false; ++ // can't break, we need to retain vanilla behavior by ensuring ALL chunks are loaded ++ } ++ } ++ } ++ } ++ } ++ if (noneMatch) { ++ // Paper end - remove expensive streams from here + if (this.remainingFireTicks <= 0) { + this.setRemainingFireTicks(-this.getFireImmuneTicks()); + } +@@ -1193,33 +1228,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + this.setRemainingFireTicks(-this.getFireImmuneTicks()); + } + +- this.level().getProfiler().pop(); +- } +- } +- } +- +- private boolean isStateClimbable(BlockState state) { +- return state.is(BlockTags.CLIMBABLE) || state.is(Blocks.POWDER_SNOW); +- } +- +- private boolean vibrationAndSoundEffectsFromBlock(BlockPos pos, BlockState state, boolean playSound, boolean emitEvent, Vec3 movement) { +- if (state.isAir()) { +- return false; +- } else { +- boolean flag2 = this.isStateClimbable(state); +- +- if ((this.onGround() || flag2 || this.isCrouching() && movement.y == 0.0D || this.isOnRails()) && !this.isSwimming()) { +- if (playSound) { +- this.walkingStepSound(pos, state); +- } +- +- if (emitEvent) { +- this.level().gameEvent(GameEvent.STEP, this.position(), GameEvent.Context.of(this, state)); +- } +- +- return true; +- } else { +- return false; ++ this.level.getProfiler().pop(); + } + } + // Paper start - detailed watchdog information +@@ -1359,32 +1368,78 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + private Vec3 collide(Vec3 movement) { +- AABB axisalignedbb = this.getBoundingBox(); +- List list = this.level().getEntityCollisions(this, axisalignedbb.expandTowards(movement)); +- Vec3 vec3d1 = movement.lengthSqr() == 0.0D ? movement : Entity.collideBoundingBox(this, movement, axisalignedbb, this.level(), list); +- boolean flag = movement.x != vec3d1.x; +- boolean flag1 = movement.y != vec3d1.y; +- boolean flag2 = movement.z != vec3d1.z; +- boolean flag3 = this.onGround() || flag1 && movement.y < 0.0D; +- +- if (this.maxUpStep() > 0.0F && flag3 && (flag || flag2)) { +- Vec3 vec3d2 = Entity.collideBoundingBox(this, new Vec3(movement.x, (double) this.maxUpStep(), movement.z), axisalignedbb, this.level(), list); +- Vec3 vec3d3 = Entity.collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep(), 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level(), list); +- +- if (vec3d3.y < (double) this.maxUpStep()) { +- Vec3 vec3d4 = Entity.collideBoundingBox(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level(), list).add(vec3d3); +- +- if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { +- vec3d2 = vec3d4; ++ // Paper start - optimise collisions ++ // This is a copy of vanilla's except that it uses strictly AABB math ++ if (movement.x == 0.0 && movement.y == 0.0 && movement.z == 0.0) { ++ return movement; ++ } ++ ++ final Level world = this.level; ++ final AABB currBoundingBox = this.getBoundingBox(); ++ ++ if (io.papermc.paper.util.CollisionUtil.isEmpty(currBoundingBox)) { ++ return movement; ++ } ++ ++ final List potentialCollisions = io.papermc.paper.util.CachedLists.getTempCollisionList(); ++ try { ++ final double stepHeight = (double)this.maxUpStep(); ++ final AABB collisionBox; ++ ++ if (movement.x == 0.0 && movement.z == 0.0 && movement.y != 0.0) { ++ if (movement.y > 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutUpwards(currBoundingBox, movement.y); ++ } else { ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutDownwards(currBoundingBox, movement.y); ++ } ++ } else { ++ if (stepHeight > 0.0 && (this.onGround || (movement.y < 0.0)) && (movement.x != 0.0 || movement.z != 0.0)) { ++ // don't bother getting the collisions if we don't need them. ++ if (movement.y <= 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(movement.x, movement.y, movement.z), stepHeight); ++ } else { ++ collisionBox = currBoundingBox.expandTowards(movement.x, Math.max(stepHeight, movement.y), movement.z); ++ } ++ } else { ++ collisionBox = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); + } + } + +- if (vec3d2.horizontalDistanceSqr() > vec3d1.horizontalDistanceSqr()) { +- return vec3d2.add(Entity.collideBoundingBox(this, new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), axisalignedbb.move(vec3d2), this.level(), list)); ++ io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, this.level.paperConfig().chunks.preventMovingIntoUnloadedChunks, ++ false, false, null, null); ++ ++ if (io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(world.getWorldBorder(), collisionBox)) { ++ io.papermc.paper.util.CollisionUtil.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions); + } +- } + +- return vec3d1; ++ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisions); ++ ++ if (stepHeight > 0.0 ++ && (this.onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) ++ && (limitedMoveVector.x != movement.x || limitedMoveVector.z != movement.z)) { ++ Vec3 vec3d2 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, stepHeight, movement.z), currBoundingBox, potentialCollisions); ++ final Vec3 vec3d3 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(movement.x, 0.0, movement.z), potentialCollisions); ++ ++ if (vec3d3.y < stepHeight) { ++ final Vec3 vec3d4 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, 0.0D, movement.z), currBoundingBox.move(vec3d3), potentialCollisions).add(vec3d3); ++ ++ if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { ++ vec3d2 = vec3d4; ++ } ++ } ++ ++ if (vec3d2.horizontalDistanceSqr() > limitedMoveVector.horizontalDistanceSqr()) { ++ return vec3d2.add(io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisions)); ++ } ++ ++ return limitedMoveVector; ++ } else { ++ return limitedMoveVector; ++ } ++ } finally { ++ io.papermc.paper.util.CachedLists.returnTempCollisionList(potentialCollisions); ++ } ++ // Paper end - optimise collisions + } + + public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, List collisions) { +@@ -2578,11 +2633,31 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + float f = this.dimensions.width * 0.8F; + AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f); + +- return BlockPos.betweenClosedStream(axisalignedbb).anyMatch((blockposition) -> { +- BlockState iblockdata = this.level().getBlockState(blockposition); + +- return !iblockdata.isAir() && iblockdata.isSuffocating(this.level(), blockposition) && Shapes.joinIsNotEmpty(iblockdata.getCollisionShape(this.level(), blockposition).move((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), Shapes.create(axisalignedbb), BooleanOp.AND); +- }); ++ BlockPos.MutableBlockPos blockposition = new BlockPos.MutableBlockPos(); ++ int minX = Mth.floor(axisalignedbb.minX); ++ int minY = Mth.floor(axisalignedbb.minY); ++ int minZ = Mth.floor(axisalignedbb.minZ); ++ int maxX = Mth.floor(axisalignedbb.maxX); ++ int maxY = Mth.floor(axisalignedbb.maxY); ++ int maxZ = Mth.floor(axisalignedbb.maxZ); ++ for (int fz = minZ; fz <= maxZ; ++fz) { ++ for (int fx = minX; fx <= maxX; ++fx) { ++ for (int fy = minY; fy <= maxY; ++fy) { ++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)this.level.getChunkIfLoadedImmediately(fx >> 4, fz >> 4); ++ if (chunk == null) { ++ continue; ++ } ++ ++ BlockState iblockdata = chunk.getBlockStateFinal(fx, fy, fz); ++ blockposition.set(fx, fy, fz); ++ if (!iblockdata.isAir() && iblockdata.isSuffocating(this.level, blockposition) && Shapes.joinIsNotEmpty(iblockdata.getCollisionShape(this.level, blockposition).move((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), Shapes.create(axisalignedbb), BooleanOp.AND)) { ++ return true; ++ } ++ } ++ } ++ } ++ return false; + } + } + diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java index a3eaf80b020c3bbc0306c5d17659ee661dfd275b..1b6f72932fbdd567a1534bcf15e8a610b00f974d 100644 --- a/src/main/java/net/minecraft/world/level/BlockCollisions.java @@ -1306,7 +1531,7 @@ index 66a5783e2a83c75ca46d1fd6f97d9de733c01a09..d860ddae508f53d06f74d8ae0efdfc50 return List.of(); } else { diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index a9e8d9c65a809562d4768df348dcd79bec4d0e3c..a8fbb4094fb0ff4d0204b477764ca210afc6a7f8 100644 +index a9e8d9c65a809562d4768df348dcd79bec4d0e3c..27680befd64967ef3c2ae0f35c9e7bd68d474314 100644 --- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -863,6 +863,12 @@ public abstract class BlockBehaviour implements FeatureElement { @@ -1322,11 +1547,158 @@ index a9e8d9c65a809562d4768df348dcd79bec4d0e3c..a8fbb4094fb0ff4d0204b477764ca210 public void initCache() { this.fluidState = ((Block) this.owner).getFluidState(this.asState()); +@@ -874,6 +880,35 @@ public abstract class BlockBehaviour implements FeatureElement { + this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - starlight - cache opacity for light + + this.legacySolid = this.calculateSolid(); ++ // Paper start ++ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(this)) { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_SPECIAL_BLOCK; ++ } else { ++ try { ++ // There is NOTHING HACKY ABOUT THIS AT ALLLLLLLLLLLLLLL ++ VoxelShape constantShape = this.getCollisionShape(null, null, null); ++ if (constantShape == null) { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK; ++ } else { ++ constantShape = constantShape.optimize(); ++ if (constantShape.isEmpty()) { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_EMPTY_BLOCK; ++ } else { ++ final List boxes = constantShape.toAabbs(); ++ if (constantShape == net.minecraft.world.phys.shapes.Shapes.getFullUnoptimisedCube() || (boxes.size() == 1 && boxes.get(0).equals(net.minecraft.world.phys.shapes.Shapes.BLOCK_OPTIMISED.aabb))) { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_FULL_BLOCK; ++ } else { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK; ++ } ++ } ++ } ++ } catch (final Error error) { ++ throw error; ++ } catch (final Throwable throwable) { ++ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_UNKNOWN_BLOCK; ++ } ++ } ++ // Paper end + } + + public Block getBlock() { diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index 5d53b09e19b664fad337ea5098bf9cf41a7168f8..dd08c2682bdc122309ff710ff6c9a0b17be41c07 100644 +index 5d53b09e19b664fad337ea5098bf9cf41a7168f8..f150a16fbf888455301d8b9043f0b45fb1fa1d84 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -59,8 +59,8 @@ public class LevelChunkSection { +@@ -39,6 +39,110 @@ public class LevelChunkSection { + this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); + } + ++ // Paper start ++ protected int specialCollidingBlocks; ++ // blockIndex = x | (z << 4) | (y << 8) ++ private long[] knownBlockCollisionData; ++ ++ private long[] initKnownDataField() { ++ return this.knownBlockCollisionData = new long[16 * 16 * 16 * 2 / Long.SIZE]; ++ } ++ ++ public final boolean hasSpecialCollidingBlocks() { ++ return this.specialCollidingBlocks != 0; ++ } ++ ++ public static long getKnownBlockInfo(final int blockIndex, final long value) { ++ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)); ++ ++ return (value >>> (valueShift << 1)) & 0b11L; ++ } ++ ++ public final long getKnownBlockInfo(final int blockIndex) { ++ if (this.knownBlockCollisionData == null) { ++ return 0L; ++ } ++ ++ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2) ++ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)); ++ ++ final long value = this.knownBlockCollisionData[arrayIndex]; ++ ++ return (value >>> (valueShift << 1)) & 0b11L; ++ } ++ ++ // important detail: this returns 32 values, one for localZ = localZ & (~1) and one for localZ = localZ | 1 ++ // the even localZ is the lower 32 bits, the odd is the upper 32 bits ++ public final long getKnownBlockInfoHorizontalRaw(final int localY, final int localZ) { ++ if (this.knownBlockCollisionData == null) { ++ return 0L; ++ } ++ ++ final int horizontalIndex = (localZ << 4) | (localY << 8); ++ return this.knownBlockCollisionData[horizontalIndex >>> (6 - 1)]; ++ } ++ ++ private void initBlockCollisionData() { ++ this.specialCollidingBlocks = 0; ++ // In 1.18 all sections will be initialised, whether or not they have blocks (fucking stupid btw) ++ // This means we can't aggressively initialise the backing long[], or else memory usage will just skyrocket. ++ // So only init if we contain non-empty blocks. ++ if (this.nonEmptyBlockCount == 0) { ++ this.knownBlockCollisionData = null; ++ return; ++ } ++ this.initKnownDataField(); ++ for (int index = 0; index < (16 * 16 * 16); ++index) { ++ final BlockState state = this.states.get(index); ++ this.setKnownBlockInfo(index, state); ++ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(state)) { ++ ++this.specialCollidingBlocks; ++ } ++ } ++ } ++ ++ // only use for initBlockCollisionData ++ private void setKnownBlockInfo(final int blockIndex, final BlockState blockState) { ++ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2) ++ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)) << 1; ++ ++ long value = this.knownBlockCollisionData[arrayIndex]; ++ ++ value &= ~(0b11L << valueShift); ++ value |= blockState.getBlockCollisionBehavior() << valueShift; ++ ++ this.knownBlockCollisionData[arrayIndex] = value; ++ } ++ ++ public void updateKnownBlockInfo(final int blockIndex, final BlockState from, final BlockState to) { ++ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(from)) { ++ --this.specialCollidingBlocks; ++ } ++ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(to)) { ++ ++this.specialCollidingBlocks; ++ } ++ ++ if (this.nonEmptyBlockCount == 0) { ++ this.knownBlockCollisionData = null; ++ return; ++ } ++ ++ if (this.knownBlockCollisionData == null) { ++ this.initKnownDataField(); ++ } ++ ++ final int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2) ++ final int valueShift = (blockIndex & (Long.SIZE / 2 - 1)) << 1; ++ ++ long value = this.knownBlockCollisionData[arrayIndex]; ++ ++ value &= ~(0b11L << valueShift); ++ value |= to.getBlockCollisionBehavior() << valueShift; ++ ++ this.knownBlockCollisionData[arrayIndex] = value; ++ } ++ // Paper end ++ + public BlockState getBlockState(int x, int y, int z) { + return (BlockState) this.states.get(x, y, z); + } +@@ -59,8 +163,8 @@ public class LevelChunkSection { return this.setBlockState(x, y, z, state, true); } @@ -1337,7 +1709,7 @@ index 5d53b09e19b664fad337ea5098bf9cf41a7168f8..dd08c2682bdc122309ff710ff6c9a0b1 if (lock) { iblockdata1 = (BlockState) this.states.getAndSet(x, y, z, state); -@@ -99,6 +99,7 @@ public class LevelChunkSection { +@@ -99,6 +203,7 @@ public class LevelChunkSection { ++this.tickingFluidCount; } @@ -1345,7 +1717,7 @@ index 5d53b09e19b664fad337ea5098bf9cf41a7168f8..dd08c2682bdc122309ff710ff6c9a0b1 return iblockdata1; } -@@ -144,6 +145,7 @@ public class LevelChunkSection { +@@ -144,6 +249,7 @@ public class LevelChunkSection { }); // Paper end diff --git a/patches/server/0741-Forward-CraftEntity-in-teleport-command.patch b/patches/server/0741-Forward-CraftEntity-in-teleport-command.patch index 28e311921b..d2fcdcaedc 100644 --- a/patches/server/0741-Forward-CraftEntity-in-teleport-command.patch +++ b/patches/server/0741-Forward-CraftEntity-in-teleport-command.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Forward CraftEntity in teleport command diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6068d70f5eb566396d8c6bf6137e2c6cf21e33cf..f4edaa4395a0ec0d6c507ad7231c3992ab812d1c 100644 +index 3f3be1147ef75918eeb3004fe191e40f59fec754..8cddb06848f7c83adde884549a94c99379ed7465 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3321,6 +3321,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3396,6 +3396,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } public void restoreFrom(Entity original) { @@ -22,7 +22,7 @@ index 6068d70f5eb566396d8c6bf6137e2c6cf21e33cf..f4edaa4395a0ec0d6c507ad7231c3992 CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag()); nbttagcompound.remove("Dimension"); -@@ -3402,10 +3409,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { +@@ -3477,10 +3484,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { if (worldserver.getTypeKey() == LevelStem.END) { // CraftBukkit ServerLevel.makeObsidianPlatform(worldserver, this); // CraftBukkit }