From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik <40902469+Samsuik@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:03:08 +0000 Subject: [PATCH] Configure cannon physics by version diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java index 299237a0c828e48425cc35a14d366020c78daefb..8014ebfb391825c31d9d1b39f5304a7e76b1ee44 100644 --- a/src/main/java/io/papermc/paper/util/CollisionUtil.java +++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java @@ -1457,7 +1457,15 @@ public final class CollisionUtil { return new Vec3(x, y, z); } + // Sakura start - physics version api public static Vec3 performAABBCollisions(final Vec3 moveVector, AABB axisalignedbb, final List potentialCollisions) { + return performAABBCollisions(moveVector, axisalignedbb, potentialCollisions, null); + } + public static Vec3 performAABBCollisions(final Vec3 moveVector, + AABB axisalignedbb, + final List potentialCollisions, + final me.samsuik.sakura.physics.PhysicsVersion physics) { + // Sakura end - physics version api double x = moveVector.x; double y = moveVector.y; double z = moveVector.z; @@ -1469,7 +1477,10 @@ public final class CollisionUtil { } } - final boolean xSmaller = Math.abs(x) < Math.abs(z); + // Sakura start - physics version api + final boolean xSmaller = physics == null || physics.afterOrEqual(1_14_0) ? Math.abs(x) < Math.abs(z) + : physics.isLegacy() && Math.abs(x) > Math.abs(z); + // Sakura end - physics version api if (xSmaller && z != 0.0) { z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions); @@ -1495,9 +1506,17 @@ public final class CollisionUtil { public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, final List voxels, final List aabbs) { + // Sakura start - physics version api + return performCollisions(moveVector, axisalignedbb, voxels, aabbs, null); + } + public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, + final List voxels, + final List aabbs, + final me.samsuik.sakura.physics.PhysicsVersion physics) { if (voxels.isEmpty()) { // fast track only AABBs - return performAABBCollisions(moveVector, axisalignedbb, aabbs); + return performAABBCollisions(moveVector, axisalignedbb, aabbs, physics); + // Sakura end - physics version api } double x = moveVector.x; @@ -1512,7 +1531,10 @@ public final class CollisionUtil { } } - final boolean xSmaller = Math.abs(x) < Math.abs(z); + // Sakura start - physics version api + final boolean xSmaller = physics == null || physics.afterOrEqual(1_14_0) ? Math.abs(x) < Math.abs(z) + : physics.isLegacy() && Math.abs(x) > Math.abs(z); + // Sakura end - physics version api if (xSmaller && z != 0.0) { z = performAABBCollisionsZ(axisalignedbb, z, aabbs); diff --git a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java index d15eb10fa203236060b90c5fc1364564dec8753f..d67f9d241f6070a2c391df52146d48434e7e096a 100644 --- a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java @@ -172,9 +172,15 @@ public abstract class SpecialisedExplosion extends Explosion { if (distanceFromBottom <= 1.0) { double x = entity.getX() - pos.x; - double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y; + double y = entity.getEyeY() - pos.y; // Sakura - physics version api double z = entity.getZ() - pos.z; double distance = Math.sqrt(x * x + y * y + z * z); + // Sakura start - physics version api + if (this.physics.before(1_17_0)) { + distanceFromBottom = (float) distanceFromBottom; + distance = (float) distance; + } + // Sakura end - physics version api if (distance != 0.0D) { x /= distance; diff --git a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java index 6bcef992766b901db34494a4d359ba335705b689..f25cbe7a1b79a6ac346a665074dce3239127b181 100644 --- a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java @@ -37,6 +37,13 @@ public final class TntExplosion extends SpecialisedExplosion { this.bounds = new AABB(x, y, z, x, y, z); } + // Sakura start - physics version api + @Override + protected double getExplosionOffset() { + return this.physics.before(1_10_0) ? (double) 0.49f : super.getExplosionOffset(); + } + // Sakura end - physics version api + @Override protected void startExplosion() { for (int i = this.calculateExplosionPotential() - 1; i >= 0; --i) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index 035071ff75fe682f3cdb8b7dbf17697beb7500c3..b336fe34b8d3e54dd300509812a6c4886105b299 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -381,7 +381,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private final double[] pistonDeltas; private long pistonDeltasGameTime; private EntityDimensions dimensions; - private float eyeHeight; + protected float eyeHeight; // Sakura - physics version public boolean isInPowderSnow; public boolean wasInPowderSnow; public boolean wasOnFire; @@ -719,6 +719,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Sakura end - cannon entity merging public boolean pushedByFluid = true; // Sakura - entity pushed by fluid api + // Sakura start - physics version api + protected me.samsuik.sakura.physics.PhysicsVersion physics = me.samsuik.sakura.physics.PhysicsVersion.LATEST; + + public final me.samsuik.sakura.physics.PhysicsVersion physics() { + return this.physics; + } + // Sakura end - physics version api public Entity(EntityType type, Level world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); @@ -1226,7 +1233,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } protected void checkSupportingBlock(boolean onGround, @Nullable Vec3 movement) { - if (onGround) { + if (onGround && this.physics.afterOrEqual(1_20_0)) { // Sakura - physics version AABB axisalignedbb = this.getBoundingBox(); AABB axisalignedbb1 = new AABB(axisalignedbb.minX, axisalignedbb.minY - 1.0E-6D, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxZ); Optional optional = this.level.findSupportingBlock(this, axisalignedbb1); @@ -1288,7 +1295,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { - if (movementType == MoverType.PISTON) { // Paper + if (movementType == MoverType.PISTON && this.physics.afterOrEqual(1_11_0)) { // Sakura - physics version api // Paper movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; @@ -1306,10 +1313,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collideScan(movement); double d0 = vec3d1.lengthSqr(); - if (d0 > 1.0E-7D) { + if (d0 > 1.0E-7D || this.physics.before(1_14_0)) { // Sakura - physics version api // NOTE: if there are any blocks in the future that rely on fall distance make sure this is correct. // The only block I am aware of is powdered snow that has a special case for falling blocks. - if (this.fallDistance != 0.0F && d0 >= 1.0D && !isFallingBlock) { + if (this.fallDistance != 0.0F && d0 >= 1.0D && !isFallingBlock && this.physics.afterOrEqual(1_18_2)) { // Sakura - physics version api BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); if (movingobjectpositionblock.getType() != HitResult.Type.MISS) { @@ -1345,6 +1352,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); + // Sakura start - physics version api + if (flag && flag1 && this.physics.isWithin(1_14_0, 1_18_1)) { + flag = false; + } + // Sakura end + this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } @@ -1386,7 +1399,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { this.wasOnFire = this.isOnFire(); - if (movementType == MoverType.PISTON) { + if (movementType == MoverType.PISTON && this.physics.afterOrEqual(1_11_0)) { // Sakura - physics version api this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper movement = this.limitPistonMovement(movement); @@ -1413,8 +1426,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess Vec3 vec3d1 = this.collide(movement); double d0 = vec3d1.lengthSqr(); - if (d0 > 1.0E-7D) { - if (this.fallDistance != 0.0F && d0 >= 1.0D) { + if (d0 > 1.0E-7D || this.physics.before(1_14_0)) { // Sakura - physics version api + if (this.fallDistance != 0.0F && d0 >= 1.0D && this.physics.afterOrEqual(1_18_2)) { // Sakura - physics version api BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); if (movingobjectpositionblock.getType() != HitResult.Type.MISS) { @@ -1450,6 +1463,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.horizontalCollision) { Vec3 vec3d2 = this.getDeltaMovement(); + // Sakura start - physics version api + if (flag && flag1 && this.physics.isWithin(1_14_0, 1_18_1)) { + flag = false; + } + // Sakura end + this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z); } @@ -1764,7 +1783,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } else { final AABB bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); collectCollisions(bb, potentialCollisionsVoxel, potentialCollisionsBB); - return io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); + return io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api } } @@ -1773,7 +1792,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess double y = movement.y; double z = movement.z; - final boolean xSmaller = Math.abs(x) < Math.abs(z); + // Sakura start - physics version api + final boolean xSmaller = this.physics == null || this.physics.afterOrEqual(1_14_0) ? Math.abs(x) < Math.abs(z) + : this.physics.isLegacy() && Math.abs(x) > Math.abs(z); + // Sakura end - physics version api if (y != 0.0) { y = scanY(currBoundingBox, y, voxelList, bbList); @@ -1889,7 +1911,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return movement; } - final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB); + final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - physics version api if (stepHeight > 0.0 && (onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) @@ -2005,8 +2027,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess protected void checkInsideBlocks() { AABB axisalignedbb = this.getBoundingBox(); - BlockPos blockposition = BlockPos.containing(axisalignedbb.minX + 1.0E-7D, axisalignedbb.minY + 1.0E-7D, axisalignedbb.minZ + 1.0E-7D); - BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - 1.0E-7D, axisalignedbb.maxY - 1.0E-7D, axisalignedbb.maxZ - 1.0E-7D); + // Sakura start - physics version + double offset = this.physics.afterOrEqual(1_19_3) ? 1.0E-7D : 0.001D; + BlockPos blockposition = BlockPos.containing(axisalignedbb.minX + offset, axisalignedbb.minY + offset, axisalignedbb.minZ + offset); + BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - offset, axisalignedbb.maxY - offset, axisalignedbb.maxZ - offset); + // Sakura end if (this.level().hasChunksAt(blockposition, blockposition1)) { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java index f682495067c1424bcf403e9e9b1f73e66f5e6c71..7500cd7647216d0101804d4442b1fd8b4c816aed 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -90,6 +90,8 @@ public class FallingBlockEntity extends Entity { this.yo = y; this.zo = z; this.setStartPos(this.blockPosition()); + this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; // Sakura + this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; // Sakura } public static FallingBlockEntity fall(Level world, BlockPos pos, BlockState state) { @@ -102,7 +104,11 @@ public class FallingBlockEntity extends Entity { FallingBlockEntity entityfallingblock = new FallingBlockEntity(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, iblockdata.hasProperty(BlockStateProperties.WATERLOGGED) ? (BlockState) iblockdata.setValue(BlockStateProperties.WATERLOGGED, false) : iblockdata); if (!CraftEventFactory.callEntityChangeBlockEvent(entityfallingblock, blockposition, iblockdata.getFluidState().createLegacyBlock())) return entityfallingblock; // CraftBukkit - world.setBlock(blockposition, iblockdata.getFluidState().createLegacyBlock(), 3); + // Sakura start - physics version api + if (entityfallingblock.physics.afterOrEqual(1_18_2)) { + world.setBlock(blockposition, iblockdata.getFluidState().createLegacyBlock(), 3); + } + // Sakura end world.addFreshEntity(entityfallingblock, spawnReason); // CraftBukkit return entityfallingblock; } @@ -191,13 +197,50 @@ public class FallingBlockEntity extends Entity { // Sakura start @Override public final double getEyeY() { - return heightParity ? getY() : super.getEyeY(); + return heightParity ? this.getY() : super.getEyeY(); } // Sakura end + // Sakura start - physics version api + @Override + public final double distanceToSqr(Vec3 vector) { + if (!this.physics.isLegacy()) + return super.distanceToSqr(vector); + double x = this.getX() - vector.x; + double y = this.getEyeY() - vector.y; + double z = this.getZ() - vector.z; + return x * x + y * y + z * z; + } + + private BlockPos patchedBlockPosition() { + // mitigate the floating point issue for sand breaking below y-0 + // 1.0e-12 allows tech that uses indirect collision clipping to still function + return BlockPos.containing(this.getX(), this.getY() + 1.0e-12, this.getZ()); + } + + private boolean isAbleToStackOnBlock() { + BlockPos pos = BlockPos.containing(this.getX(), this.getY() - 0.001f, this.getZ()); + BlockState state = this.level().getBlockState(pos); + return !FallingBlock.isFree(state); + } + + private void removeBlockOnFall(Block block) { + BlockPos blockposition = this.blockPosition(); + BlockState blockstate = this.level().getBlockState(blockposition); + + if (blockstate.is(block) && CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, Blocks.AIR.defaultBlockState())) { + this.level().removeBlock(blockposition, false); + } else { + if (blockstate.is(block)) { + ((ServerLevel) level()).getChunkSource().blockChanged(blockposition); + } + this.discard(EntityRemoveEvent.Cause.DESPAWN); + } + } + // Sakura end - physics version api @Override protected double getDefaultGravity() { - return 0.04D; + return this.physics.before(1_14_0) ? 0.04F : 0.04D; // Sakura - physics version api } @Override @@ -207,6 +250,11 @@ public class FallingBlockEntity extends Entity { } else { Block block = this.blockState.getBlock(); + // Sakura start - physics version api + if (this.time == 0 && this.physics.before(1_18_2)) { + this.removeBlockOnFall(block); + } + // Sakura end - physics version api ++this.time; this.applyGravity(); this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement @@ -220,8 +268,15 @@ public class FallingBlockEntity extends Entity { return; } // Paper end - Configurable falling blocks height nerf + // Sakura start - physics version api + if (this.physics.before(1_12_0)) { + this.scaleDeltaMovement(0.98F); + } if (!this.level().isClientSide) { - BlockPos blockposition = this.blockPosition(); + // Patching the floating point issue on modern versions can break some cannons that rely on it. + // However, it makes sense for legacy versions pre-1.17 before the world height change. + BlockPos blockposition = this.physics.before(1_17_0) ? this.patchedBlockPosition() : this.blockPosition(); + // Sakura end - physics version api boolean flag = this.blockState.getBlock() instanceof ConcretePowderBlock; boolean flag1 = flag && this.level().getFluidState(blockposition).is(FluidTags.WATER); double d0 = this.getDeltaMovement().lengthSqr(); @@ -246,8 +301,11 @@ public class FallingBlockEntity extends Entity { } else { BlockState iblockdata = this.level().getBlockState(blockposition); - this.multiplyDeltaMovement(0.7D, -0.5D, 0.7D); // Sakura - reduce movement allocations - if (!iblockdata.is(Blocks.MOVING_PISTON)) { + // Sakura start - physics version api + double friction = this.physics.before(1_14_0) ? 0.7F : 0.7D; + this.multiplyDeltaMovement(friction, -0.5D, friction); // Sakura - reduce movement allocations + if (!iblockdata.is(Blocks.MOVING_PISTON) && (flag1 || !this.physics.isWithin(1_9_0, 1_12_0) || this.isAbleToStackOnBlock())) { + // Sakura end - physics version api if (!this.cancelDrop) { boolean flag2 = iblockdata.canBeReplaced((BlockPlaceContext) (new DirectionalPlaceContext(this.level(), blockposition, Direction.DOWN, ItemStack.EMPTY, Direction.UP))); boolean flag3 = FallingBlock.isFree(this.level().getBlockState(blockposition.below())) && (!flag || !flag1); @@ -314,7 +372,12 @@ public class FallingBlockEntity extends Entity { } } - this.scaleDeltaMovement(0.98D); // Sakura - reduce movement allocations + // Sakura start - physics version api + if (physics.afterOrEqual(1_12_0)) { + double drag = physics.before(1_14_0) ? 0.98F : 0.98D; + this.scaleDeltaMovement(drag); // Sakura - reduce movement allocations + } + // Sakura end - physics version api } } diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java index df2a37e57012333a618937e61cb5a99c3ef4a0f9..62d40333c42f673479db5e6312343161aacf7078 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -58,6 +58,13 @@ public class PrimedTnt extends Entity implements TraceableEntity { case Y -> multiplyDeltaMovement(0, 1, 0); } // Sakura end + // Sakura start - physics version api + this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; + this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; + if (this.physics.isLegacy()) { + multiplyDeltaMovement(0, 1, 0); + } + // Sakura end - physics version api } @Override @@ -105,10 +112,26 @@ public class PrimedTnt extends Entity implements TraceableEntity { } } // Sakura end + // Sakura start - physics version api + @Override + public final double getEyeY() { + return this.physics.isLegacy() ? super.getEyeY() : this.getY(); + } + + @Override + public final double distanceToSqr(net.minecraft.world.phys.Vec3 vector) { + if (!this.physics.isLegacy()) + return super.distanceToSqr(vector); + double x = this.getX() - vector.x; + double y = this.getEyeY() - vector.y; + double z = this.getZ() - vector.z; + return x * x + y * y + z * z; + } + // Sakura end - physics version api @Override protected double getDefaultGravity() { - return 0.04D; + return this.physics.before(1_14_0) ? 0.04F : 0.04D; // Sakura - physics version api } @Override @@ -122,15 +145,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { return; } // Paper end - Configurable TNT height nerf - this.scaleDeltaMovement(0.98D); // Sakura - reduce movement allocations + // Sakura start - physics version api + double drag = this.physics.before(1_14_0) ? 0.98F : 0.98D; + this.scaleDeltaMovement(drag); // Sakura - reduce movement allocations if (this.onGround()) { - this.multiplyDeltaMovement(0.7D, -0.5D, 0.7D); // Sakura - reduce movement allocations + double friction = this.physics.before(1_14_0) ? 0.7F : 0.7D; + this.multiplyDeltaMovement(friction, -0.5D, friction); // Sakura - reduce movement allocations + // Sakura end - physics version api } int i = this.getFuse() - 1; this.setFuse(i); - if (i <= 0) { + if (this.physics.before(1_9_0) ? (i < 0) : (i <= 0)) { // Sakura - physics version api // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event // this.discard(); this.respawnMerged(); // Sakura @@ -183,7 +210,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive)this.getBukkitEntity()); if (!event.isCancelled()) { - this.level().explode(this, this.getX(), this.getY(0.0625D), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); + // Sakura start - physics version api + double pos = this.physics.before(1_10_0) ? this.getY() + (double) 0.49f : this.getY(0.0625D); + this.level().explode(this, this.getX(), pos, this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.TNT); + // Sakura end } // CraftBukkit end } @@ -237,7 +267,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { - return !level().paperConfig().fixes.preventTntFromMovingInWater && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - convenience + return !level().paperConfig().fixes.preventTntFromMovingInWater && !this.physics.isLegacy() && level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - physics version // Sakura - convenience } // Paper end - Option to prevent TNT from moving in water } diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java index e0a9284bc0760306f1b295f35433f98c628b288a..6932856c7c9c41b6c7979c042db47f799c2f16cc 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -76,6 +76,7 @@ public class Explosion { public float yield; // CraftBukkit end private final boolean consistentRadius; // Sakura - consistent explosion radius + protected final me.samsuik.sakura.physics.PhysicsVersion physics; // Sakura - physics version api public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); @@ -114,6 +115,7 @@ public class Explosion { this.explosionSound = soundEvent; this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit this.consistentRadius = world.localConfig().config(BlockPos.containing(x, y, z)).consistentRadius; // Sakura - consistent explosion radius + this.physics = entity != null ? entity.physics() : world.localConfig().config(BlockPos.containing(x, y, z)).physicsVersion; // Sakura - physics version api } // Sakura start - optimise paper explosions @@ -507,8 +509,12 @@ public class Explosion { final float density = entity.level().densityCache.getKnownDensity(vec3d1); if (density != me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { hitResult = density != 0.0f ? net.minecraft.world.phys.HitResult.Type.MISS : net.minecraft.world.phys.HitResult.Type.BLOCK; + // Sakura start - physics version api + } else if (entity.physics().before(1_14_0)) { + hitResult = entity.level().rayTrace(vec3d1, source); } else { - hitResult = entity.level().clip(new ClipContext(vec3d1, source, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType(); + hitResult = entity.level().clip(new ClipContext(vec3d1, source, entity.physics().afterOrEqual(1_16_0) ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity)).getType(); + // Sakura end - physics version api } if (hitResult == HitResult.Type.MISS) { // Sakura end - replace density cache @@ -613,6 +619,14 @@ public class Explosion { } if (cachedBlock.outOfWorld) { + // Sakura start - physics version api + if (this.physics.before(1_17_0)) { + d4 += d0; + d5 += d1; + d6 += d2; + continue; + } + // Sakura end - physics version api break; } @@ -718,9 +732,15 @@ public class Explosion { if (d7 <= 1.0D) { double d8 = entity.getX() - this.x; - double d9 = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y; + double d9 = entity.getEyeY() - this.y; // Sakura - physics version api double d10 = entity.getZ() - this.z; double d11 = Math.sqrt(d8 * d8 + d9 * d9 + d10 * d10); + // Sakura start - physics version api + if (this.physics.before(1_17_0)) { + d7 = (float) d7; + d11 = (float) d11; + } + // Sakura end - physics version api if (d11 != 0.0D) { d8 /= d11; @@ -1047,7 +1067,7 @@ public class Explosion { // Sakura start - replace density cache float blockDensity = this.level.densityCache.getDensity(vec3d, entity); if (blockDensity == me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { - blockDensity = this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions; + blockDensity = this.getSeenPercent(vec3d, entity, blockCache, blockPos); // Sakura - physics version api // Paper - optimise explosions; this.level.densityCache.putDensity(vec3d, entity, blockDensity); // Sakura end - replace density cache } @@ -1055,6 +1075,17 @@ public class Explosion { return blockDensity; } + // Sakura start - physics version api + private float getSeenPercent(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { + if (this.physics.afterOrEqual(1_16_0)) { + // Papers impl is untouched, intended to be used as a fast path. + return this.getSeenFraction(vec3d, entity, blockCache, blockPos); + } else { + return getSeenPercent(vec3d, entity); + } + } + // Sakura end - physics version api + static class CacheKey { private final Level world; private final double posX, posY, posZ; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index ef37c34036bc79505786ee38ab5472a45effa4a3..6f7aa220d13a1135dde24012544bf3b978af7e50 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -255,6 +255,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return this.getLimitedEntities(except, box, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS, limit, search); } // Sakura end - add entity retrival methods with search limits + // Sakura start - physics version api + public net.minecraft.world.phys.BlockHitResult.Type rayTrace(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1) { + // May deviate from vanilla here; I remember noticing a bug and there's no fix commit. + int i = Mth.floor(vec3d1.x); + int j = Mth.floor(vec3d1.y); + int k = Mth.floor(vec3d1.z); + int l = Mth.floor(vec3d.x); + int i1 = Mth.floor(vec3d.y); + int j1 = Mth.floor(vec3d.z); + BlockPos.MutableBlockPos blockposition = new BlockPos.MutableBlockPos(l, i1, j1); + LevelChunk chunk = this.getChunkIfLoaded(l >> 4, j1 >> 4); + + // probably a bad idea to copy this over so we don't need to do a null check + if (chunk == null) { + return net.minecraft.world.phys.BlockHitResult.Type.MISS; + } + + BlockState iblockdata = chunk.getBlockState(blockposition); + net.minecraft.world.phys.shapes.VoxelShape shape = iblockdata.getShape(this, blockposition); + + for (AABB bb : shape.toAabbs()) { + if (clip(bb, blockposition, vec3d, vec3d1)) { + return net.minecraft.world.phys.BlockHitResult.Type.BLOCK; + } + } + + int k1 = 200; + + while (k1-- >= 0) { + if (l == i && i1 == j && j1 == k) { + return net.minecraft.world.phys.BlockHitResult.Type.MISS; + } + + boolean flag3 = true; + boolean flag4 = true; + boolean flag5 = true; + double d0 = 999.0D; + double d1 = 999.0D; + double d2 = 999.0D; + + if (i > l) { + d0 = (double) l + 1.0D; + } else if (i < l) { + d0 = (double) l + 0.0D; + } else { + flag3 = false; + } + + if (j > i1) { + d1 = (double) i1 + 1.0D; + } else if (j < i1) { + d1 = (double) i1 + 0.0D; + } else { + flag4 = false; + } + + if (k > j1) { + d2 = (double) j1 + 1.0D; + } else if (k < j1) { + d2 = (double) j1 + 0.0D; + } else { + flag5 = false; + } + + double d3 = 999.0D; + double d4 = 999.0D; + double d5 = 999.0D; + double d6 = vec3d1.x - vec3d.x; + double d7 = vec3d1.y - vec3d.y; + double d8 = vec3d1.z - vec3d.z; + + if (flag3) { + d3 = (d0 - vec3d.x) / d6; + } + + if (flag4) { + d4 = (d1 - vec3d.y) / d7; + } + + if (flag5) { + d5 = (d2 - vec3d.z) / d8; + } + + if (d3 == -0.0D) { + d3 = -1.0E-4D; + } + + if (d4 == -0.0D) { + d4 = -1.0E-4D; + } + + if (d5 == -0.0D) { + d5 = -1.0E-4D; + } + + Direction enumdirection; + + if (d3 < d4 && d3 < d5) { + enumdirection = i > l ? Direction.WEST : Direction.EAST; + vec3d = new net.minecraft.world.phys.Vec3(d0, vec3d.y + d7 * d3, vec3d.z + d8 * d3); + } else if (d4 < d5) { + enumdirection = j > i1 ? Direction.DOWN : Direction.UP; + vec3d = new net.minecraft.world.phys.Vec3(vec3d.x + d6 * d4, d1, vec3d.z + d8 * d4); + } else { + enumdirection = k > j1 ? Direction.NORTH : Direction.SOUTH; + vec3d = new net.minecraft.world.phys.Vec3(vec3d.x + d6 * d5, vec3d.y + d7 * d5, d2); + } + + l = Mth.floor(vec3d.x) - (enumdirection == Direction.EAST ? 1 : 0); + i1 = Mth.floor(vec3d.y) - (enumdirection == Direction.UP ? 1 : 0); + j1 = Mth.floor(vec3d.z) - (enumdirection == Direction.SOUTH ? 1 : 0); + blockposition.set(l, i1, j1); + + int chunkX = l >> 4; + int chunkZ = j1 >> 4; + + if (chunkX != chunk.locX || chunkZ != chunk.locZ) { + chunk = this.getChunkIfLoaded(chunkX, chunkZ); + } + + if (chunk == null) { + return net.minecraft.world.phys.BlockHitResult.Type.MISS; + } + + iblockdata = chunk.getBlockState(blockposition); + shape = iblockdata.getShape(this, blockposition); + + for (AABB bb : shape.toAabbs()) { + if (clip(bb, blockposition, vec3d, vec3d1)) { + return net.minecraft.world.phys.BlockHitResult.Type.BLOCK; + } + } + } + + return net.minecraft.world.phys.BlockHitResult.Type.MISS; + } + + private boolean clip(AABB bb, BlockPos blockposition, net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1) { + vec3d = vec3d.subtract(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + vec3d1 = vec3d1.subtract(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + double x = vec3d1.x - vec3d.x; + double y = vec3d1.y - vec3d.y; + double z = vec3d1.z - vec3d.z; + + double minXd = clip(bb.minX, x, vec3d.x); + double minYd = clip(bb.minY, y, vec3d.y); + double minZd = clip(bb.minZ, z, vec3d.z); + double maxXd = clip(bb.maxX, x, vec3d.x); + double maxYd = clip(bb.maxY, y, vec3d.y); + double maxZd = clip(bb.maxZ, z, vec3d.z); + + return clipX(vec3d, bb, minXd, y, z) || clipY(vec3d, bb, minYd, x, z) || clipZ(vec3d, bb, minZd, x, y) + || clipX(vec3d, bb, maxXd, y, z) || clipY(vec3d, bb, maxYd, x, z) || clipZ(vec3d, bb, maxZd, x, y); + } + + private double clip(double bound, double axisD, double axisN) { + // This is my friend jerry, he was an epsilon. Unfortunately, once day + // he was cast to a float. Now he's spending his retirement here as a double. + if (axisD * axisD < 1.0000000116860974E-7D) { + return -1.0; + } + + return (bound - axisN) / axisD; + } + + private boolean clipX(net.minecraft.world.phys.Vec3 vec3d, AABB bb, double n, double y, double z) { + if (n < 0.0 || n > 1.0) { + return false; + } + + y = vec3d.y + y * n; + z = vec3d.z + z * n; + + return y >= bb.minY && y <= bb.maxY && z >= bb.minZ && z <= bb.maxZ; + } + + private boolean clipY(net.minecraft.world.phys.Vec3 vec3d, AABB bb, double n, double x, double z) { + if (n < 0.0 || n > 1.0) { + return false; + } + + x = vec3d.x + x * n; + z = vec3d.z + z * n; + + return x >= bb.minX && x <= bb.maxX && z >= bb.minZ && z <= bb.maxZ; + } + + private boolean clipZ(net.minecraft.world.phys.Vec3 vec3d, AABB bb, double n, double x, double y) { + if (n < 0.0 || n > 1.0) { + return false; + } + + x = vec3d.x + x * n; + y = vec3d.y + y * n; + + return x >= bb.minX && x <= bb.maxX && y >= bb.minY && y <= bb.maxY; + } + // Sakura end - physics version api protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura - sakura configuration files // Paper - create paper world config; Async-Anti-Xray: Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot diff --git a/src/main/java/net/minecraft/world/level/block/FallingBlock.java b/src/main/java/net/minecraft/world/level/block/FallingBlock.java index 1ea1232a5ba3e48eef3a139d6487c9a190155ebd..71364fe94cfeefa07fac3ee6359f7abd9bb58106 100644 --- a/src/main/java/net/minecraft/world/level/block/FallingBlock.java +++ b/src/main/java/net/minecraft/world/level/block/FallingBlock.java @@ -35,6 +35,15 @@ public abstract class FallingBlock extends Block implements Fallable { return super.updateShape(state, direction, neighborState, world, pos, neighborPos); } + // Sakura start - physics version api + @Override + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean notify) { + if (world.localConfig().config(pos).physicsVersion.before(1_18_2)) { + world.scheduleTick(pos, this, this.getDelayAfterPlace()); + } + } + // Sakura end + @Override protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { if (isFree(world.getBlockState(pos.below())) && pos.getY() >= world.getMinBuildHeight()) { diff --git a/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java b/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java index 79df73d352e7c72efb4b0b3ae567b60d19e2150e..7c434d7c55bec4ba1d6013109ed8fd769e8b12f0 100644 --- a/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java +++ b/src/main/java/net/minecraft/world/level/block/FenceGateBlock.java @@ -180,8 +180,13 @@ public class FenceGateBlock extends HorizontalDirectionalBlock { } // CraftBukkit end - if ((Boolean) state.getValue(FenceGateBlock.POWERED) != flag1) { - world.setBlock(pos, (BlockState) ((BlockState) state.setValue(FenceGateBlock.POWERED, flag1)).setValue(FenceGateBlock.OPEN, flag1), 2); + // Sakura start + final boolean legacy = world.localConfig().config(pos).physicsVersion.before(1_11_0); + final boolean powered = state.getValue(FenceGateBlock.POWERED); + if (legacy ? (flag1 || sourceBlock.defaultBlockState().isSignalSource()) : powered != flag1) { + final boolean openGate = legacy && (flag1 == powered || state.getValue(FenceGateBlock.OPEN) != powered) ? state.getValue(OPEN) : flag1; + world.setBlock(pos, (BlockState) ((BlockState) state.setValue(FenceGateBlock.POWERED, flag1)).setValue(FenceGateBlock.OPEN, openGate), 2); + // Sakura end if ((Boolean) state.getValue(FenceGateBlock.OPEN) != flag1) { world.playSound((Player) null, pos, flag1 ? this.type.fenceGateOpen() : this.type.fenceGateClose(), SoundSource.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F); world.gameEvent((Entity) null, (Holder) (flag1 ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE), pos); diff --git a/src/main/java/net/minecraft/world/level/block/LadderBlock.java b/src/main/java/net/minecraft/world/level/block/LadderBlock.java index 4b402a7222f78617ef7d28d329f4daac74954347..54781ea0771327f93a7cf672bb4b2945700c47e5 100644 --- a/src/main/java/net/minecraft/world/level/block/LadderBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LadderBlock.java @@ -28,6 +28,21 @@ public class LadderBlock extends Block implements SimpleWaterloggedBlock { protected static final VoxelShape WEST_AABB = Block.box(13.0, 0.0, 0.0, 16.0, 16.0, 16.0); protected static final VoxelShape SOUTH_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 3.0); protected static final VoxelShape NORTH_AABB = Block.box(0.0, 0.0, 13.0, 16.0, 16.0, 16.0); + // Sakura start - physics version api + protected static final VoxelShape LEGACY_EAST_AABB = Block.box(0.0, 0.0, 0.0, 2.0, 16.0, 16.0); + protected static final VoxelShape LEGACY_WEST_AABB = Block.box(14.0, 0.0, 0.0, 16.0, 16.0, 16.0); + protected static final VoxelShape LEGACY_SOUTH_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 2.0); + protected static final VoxelShape LEGACY_NORTH_AABB = Block.box(0.0, 0.0, 14.0, 16.0, 16.0, 16.0); + + private static VoxelShape legacyShape(Direction facing) { + return switch (facing) { + case NORTH -> LEGACY_NORTH_AABB; + case SOUTH -> LEGACY_SOUTH_AABB; + case WEST -> LEGACY_WEST_AABB; + default -> LEGACY_EAST_AABB; + }; + } + // Sakura end - physics version api @Override public MapCodec codec() { @@ -39,8 +54,18 @@ public class LadderBlock extends Block implements SimpleWaterloggedBlock { this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(WATERLOGGED, Boolean.valueOf(false))); } + // Sakura start - physics version api + @Override + public final boolean hasDynamicShape() { + return true; + } + @Override protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + if (world instanceof net.minecraft.world.level.Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + return legacyShape(state.getValue(FACING)); + } + // Sakura end - physics version api switch ((Direction)state.getValue(FACING)) { case NORTH: return NORTH_AABB; diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java index 84623c632d8c2f0fa7ec939c711316d757117d23..baf791608420198493df24c68144fd29ec9fad7f 100644 --- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java @@ -190,8 +190,26 @@ public class LiquidBlock extends Block implements BucketPickup { BlockPos blockposition1 = pos.relative(enumdirection.getOpposite()); if (world.getFluidState(blockposition1).is(FluidTags.WATER)) { - Block block = world.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; - + // Sakura start + final FluidState fluidState = state.getFluidState(); + final Block block; + + if (fluidState.isSource()) { + block = Blocks.OBSIDIAN; + } else { + final me.samsuik.sakura.physics.PhysicsVersion physics = world.localConfig().config(pos).physicsVersion; + + // SANITY: In legacy a patch by paper removes the fluid level condition from vanilla. + if (physics.afterOrEqual(1_16_0) || physics.isLegacy() + || physics.afterOrEqual(1_13_0) && fluidState.getHeight(world, pos) >= 0.44444445f + || physics.before(1_13_0) && FlowingFluid.getLegacyLevel(fluidState) <= 4 + ) { + block = Blocks.COBBLESTONE; + } else { + return true; + } + } + // Sakura end // CraftBukkit start if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, pos, block.defaultBlockState())) { this.fizz(world, pos); diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java index 88ddd1747d9786210e8faf412b3b0363df4bab43..a6f0ded367341e6b9f9c7b1c4254dd696ead2f8d 100644 --- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -699,6 +699,10 @@ public class RedStoneWireBlock extends Block { protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { if (!player.getAbilities().mayBuild) { return InteractionResult.PASS; + // Sakura start + } else if (world.localConfig().config(pos).physicsVersion.before(1_16_0)) { + return InteractionResult.PASS; + // Sakura end } else { if (RedStoneWireBlock.isCross(state) || RedStoneWireBlock.isDot(state)) { BlockState iblockdata1 = RedStoneWireBlock.isCross(state) ? this.defaultBlockState() : this.crossState; diff --git a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java index 932831bb5632ead5850842fc77192c841571162e..fdf07b8aa7a10da15a9473bcb12e8cb0404654f8 100644 --- a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +++ b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java @@ -21,6 +21,7 @@ public class WaterlilyBlock extends BushBlock { public static final MapCodec CODEC = simpleCodec(WaterlilyBlock::new); protected static final VoxelShape AABB = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 1.5D, 15.0D); + protected static final VoxelShape LEGACY_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 0.25D, 16.0D); // Sakura @Override public MapCodec codec() { @@ -46,8 +47,18 @@ public class WaterlilyBlock extends BushBlock { } + // Sakura start + @Override + public final boolean hasDynamicShape() { + return true; + } + @Override protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + if (world instanceof net.minecraft.world.level.Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + return LEGACY_AABB; + } + // Sakura end return WaterlilyBlock.AABB; } diff --git a/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java b/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java index f970fca8a8b479f2d2b927bcee2d73f02bfcd1b3..ff31e517a5f2eb51acef070d6455a0f86e40dd9f 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/MovingPistonBlock.java @@ -110,6 +110,17 @@ public class MovingPistonBlock extends BaseEntityBlock { @Override protected VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { PistonMovingBlockEntity pistonMovingBlockEntity = this.getBlockEntity(world, pos); + // Sakura start - physics version api + if (pistonMovingBlockEntity != null && world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + VoxelShape shape = pistonMovingBlockEntity.getCollisionShapeFromProgress(level, pos); + + if (context.isAbove(shape, pos, false)) { + return shape; + } else { + return pistonMovingBlockEntity.getMovedState().getCollisionShape(world, pos); + } + } + // Sakura end return pistonMovingBlockEntity != null ? pistonMovingBlockEntity.getCollisionShape(world, pos) : Shapes.empty(); } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java index e0c62227b279a5fe0f3868fbf9ce8c78c515a09c..3f840b82827b803a5fc594f6008cddb09926ca2d 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java @@ -171,6 +171,11 @@ public class PistonBaseBlock extends DirectionalBlock { // } // PAIL: checkME - what happened to setTypeAndData? // CraftBukkit end + // Sakura start - physics version api + if (world.localConfig().config(pos).physicsVersion.before(1_9_0)) { + world.setBlock(pos, state.setValue(PistonBaseBlock.EXTENDED, false), 18); + } + // Sakura end world.blockEvent(pos, this, b0, enumdirection.get3DDataValue()); } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java index bf58f33a5dc11b9e36cb9a26a73558c675a429df..755cbe2925fb5230fdff6c14b94dbafb924ba2c2 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonHeadBlock.java @@ -138,6 +138,11 @@ public class PistonHeadBlock extends DirectionalBlock { @Override protected boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { BlockState blockState = world.getBlockState(pos.relative(state.getValue(FACING).getOpposite())); + // Sakura start - physics version api + if (world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_9_0)) { + return this.isFittingBase(state, blockState); + } + // Sakura end return this.isFittingBase(state, blockState) || blockState.is(Blocks.MOVING_PISTON) && blockState.getValue(FACING) == state.getValue(FACING); } @@ -145,6 +150,10 @@ public class PistonHeadBlock extends DirectionalBlock { protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { if (state.canSurvive(world, pos)) { world.neighborChanged(pos.relative(state.getValue(FACING).getOpposite()), sourceBlock, sourcePos); + // Sakura start - physics version api + } else if (world.localConfig().config(pos).physicsVersion.before(1_9_0)) { + world.setBlock(pos, Blocks.AIR.defaultBlockState(), 19); + // Sakura end } } diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java index d555ad1dd2f648b84920eceec6cc99e8801334b3..b2ecc615379856f661ba87bdeb28f75aa7d61602 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java @@ -164,6 +164,12 @@ public class PistonMovingBlockEntity extends BlockEntity { double i = 0.0; + // Sakura start - physics version api + if (entity.physics().before(1_11_0)) { + moveEntityByPistonFromDirection(direction, entity, aABB); + return; + } + // Sakura end - physics version api for (AABB aABB2 : list2) { AABB aABB3 = PistonMath.getMovementArea(moveByPositionAndProgress(pos, aABB2, blockEntity), direction, d); AABB aABB4 = entity.getBoundingBox(); @@ -293,14 +299,167 @@ public class PistonMovingBlockEntity extends BlockEntity { } } } + + // Sakura start - physics version api + @javax.annotation.Nullable + private AABB getBoundsFromProgress(Level level, BlockPos pos, BlockState state, float progress, Direction dir, boolean absolute) { + if (!state.is(Blocks.MOVING_PISTON) && !state.isAir()) { + VoxelShape shape = this.movedState.getCollisionShape(level, pos); + // bounds on an empty shape causes an exception + if (shape.isEmpty()) return null; + if (absolute) shape = shape.move(pos.getX(), pos.getY(), pos.getZ()); + AABB bounds = shape.bounds(); + + double minX = bounds.minX; + double minY = bounds.minY; + double minZ = bounds.minZ; + double maxX = bounds.maxX; + double maxY = bounds.maxY; + double maxZ = bounds.maxZ; + + if (dir.getStepX() < 0) { + minX -= (float) dir.getStepX() * progress; + } else { + maxX -= (float) dir.getStepX() * progress; + } + + if (dir.getStepY() < 0) { + minY -= (float) dir.getStepY() * progress; + } else { + maxY -= (float) dir.getStepY() * progress; + } + + if (dir.getStepZ() < 0) { + minZ -= (float) dir.getStepZ() * progress; + } else { + maxZ -= (float) dir.getStepZ() * progress; + } + + return this.fixZeroWidthBB(new AABB(minX, minY, minZ, maxX, maxY, maxZ), dir); + } + + return null; + } + + private AABB fixZeroWidthBB(AABB bb, Direction dir) { + // Legacy behaviour relied on entities being able to collide with zero width shapes + // This is no longer possible, so we have to create a difference here for it to work + double expandX = bb.getXsize() == 0.0 ? 1.0e-5 * dir.getStepX() : 0; + double expandY = bb.getYsize() == 0.0 ? 1.0e-5 * dir.getStepY() : 0; + double expandZ = bb.getZsize() == 0.0 ? 1.0e-5 * dir.getStepZ() : 0; + + if (expandX != 0 || expandY != 0 || expandZ != 0) { + bb = bb.expandTowards(expandX, expandY, expandZ); + } + + return bb; + } + + public final VoxelShape getCollisionShapeFromProgress(Level level, BlockPos pos) { + float progress = this.getProgress(0.0f); + + if (this.extending) { + progress = 1.0F - progress; + } + + AABB bb = this.getBoundsFromProgress(level, pos, this.movedState, progress, this.direction, false); + return bb == null ? Shapes.empty() : Shapes.create(bb); + } + + private void moveEntities(Level level, float f1) { + float f = this.progress; + + if (this.extending) { + f = 1.0F - f; + } else { + --f; + } + + AABB bb = this.getBoundsFromProgress(level, this.worldPosition, this.movedState, f, this.direction, true); + + if (bb == null || bb.getSize() == 0.0) { + return; + } + + List entities = level.getEntities(null, bb); + + if (entities.isEmpty()) { + return; + } + + for (Entity entity : entities) { + if (this.movedState.is(Blocks.SLIME_BLOCK) && this.extending) { + Vec3 movement = entity.getDeltaMovement(); + double x = movement.x; + double y = movement.y; + double z = movement.z; + + switch (this.direction.getAxis()) { + case X -> x = direction.getStepX(); + case Y -> y = direction.getStepY(); + case Z -> z = direction.getStepZ(); + } + + entity.setDeltaMovement(x, y, z); + } else { + entity.move(MoverType.PISTON, new Vec3(f1 * (float) this.direction.getStepX(), f1 * (float) this.direction.getStepY(), f1 * (float) this.direction.getStepZ())); + } + } + } + + private static void moveEntityByPistonFromDirection(Direction direction, Entity entity, AABB blockBB) { + AABB entityBB = entity.getBoundingBox(); + double movX = 0.0; + double movY = 0.0; + double movZ = 0.0; + + switch (direction.getAxis()) { + case X -> { + if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) { + movX = blockBB.maxX - entityBB.minX; + } else { + movX = entityBB.maxX - blockBB.minX; + } + movX += 0.01D; + } + case Y -> { + if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) { + movY = blockBB.maxY - entityBB.minY; + } else { + movY = entityBB.maxY - blockBB.minY; + } + movY += 0.01D; + } + case Z -> { + if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) { + movZ = blockBB.maxZ - entityBB.minZ; + } else { + movZ = entityBB.maxZ - blockBB.minZ; + } + movZ += 0.01D; + } + } + + entity.move(MoverType.PISTON, new Vec3(movX * direction.getStepX(), movY * direction.getStepY(), movZ * direction.getStepZ())); + } + // Sakura end - physics version api public static void tick(Level world, BlockPos pos, BlockState state, PistonMovingBlockEntity blockEntity) { + me.samsuik.sakura.physics.PhysicsVersion physicsVersion = world.localConfig().config(pos).physicsVersion; // Sakura - physics version api blockEntity.lastTicked = world.getGameTime(); blockEntity.progressO = blockEntity.progress; if (blockEntity.progressO >= 1.0F) { if (world.isClientSide && blockEntity.deathTicks < 5) { blockEntity.deathTicks++; } else { + // Sakura start - physics version api + if (physicsVersion.isWithin(1_9_0, 1_10_0)) { + moveCollidedEntities(world, pos, 1.0f, blockEntity); + moveStuckEntities(world, pos, 1.0f, blockEntity); + } else if (physicsVersion.before(1_9_0)) { + blockEntity.moveEntities(world, 0.25f); + } + // Sakura end - physics version api world.removeBlockEntity(pos); blockEntity.setRemoved(); if (world.getBlockState(pos).is(Blocks.MOVING_PISTON)) { @@ -320,12 +479,23 @@ public class PistonMovingBlockEntity extends BlockEntity { } } else { float f = blockEntity.progress + 0.5F; + // Sakura start - physics version api + if (physicsVersion.afterOrEqual(1_11_0)) { moveCollidedEntities(world, pos, f, blockEntity); moveStuckEntities(world, pos, f, blockEntity); + } + blockEntity.progress = f; if (blockEntity.progress >= 1.0F) { blockEntity.progress = 1.0F; } + if (physicsVersion.isWithin(1_9_0, 1_10_0)) { + moveCollidedEntities(world, pos, f, blockEntity); + moveStuckEntities(world, pos, f, blockEntity); + } else if (blockEntity.extending && physicsVersion.before(1_9_0)) { + blockEntity.moveEntities(world, blockEntity.progress - blockEntity.progressO + 0.0625f); + } + // Sakura end - physics version api } } diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java index 5ad6880845ed699077ad355ef1edcfb1c6c7bee4..e6acf6c4bcdb8d2548aa6a8b8d9af7c56877cfd4 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java @@ -525,7 +525,7 @@ public abstract class FlowingFluid extends Fluid { this.spread(world, pos, state); } - protected static int getLegacyLevel(FluidState state) { + public static int getLegacyLevel(FluidState state) { // Sakura - protected -> public return state.isSource() ? 0 : 8 - Math.min(state.getAmount(), 8) + ((Boolean) state.getValue(FlowingFluid.FALLING) ? 8 : 0); } diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java index 3bb4a9a1a6249e8ba2de237f801210e7f4fd5825..8dfa05a586bd21ef8aeab713cad4628166982bfa 100644 --- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java +++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java @@ -175,7 +175,10 @@ public abstract class LavaFluid extends FlowingFluid { @Override public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - return state.getHeight(world, pos) >= 0.44444445F && fluid.is(FluidTags.WATER); + // Sakura start + return state.getHeight(world, pos) >= 0.44444445F && fluid.is(FluidTags.WATER) + && world instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); + // Sakura end } @Override diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java index 109f71401c65f476ccf6813137386fc9fef10254..786aba3810b71a543469dab6b2b2c1ff4ca2edd5 100644 --- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java +++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java @@ -120,7 +120,10 @@ public abstract class WaterFluid extends FlowingFluid { @Override public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid, Direction direction) { - return direction == Direction.DOWN && !fluid.is(FluidTags.WATER); + // Sakura start + return direction == Direction.DOWN && !fluid.is(FluidTags.WATER) + || fluid.is(FluidTags.LAVA) && world instanceof Level level && level.localConfig().config(pos).physicsVersion.before(1_13_0); + // Sakura end } @Override