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 19086bbfdf3a015eafec5ca868c8d2451f554ef0..510d722fffd4bdcee2db42aefa662c49563ffa81 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 0611555b4afb461e2045585e3d81601420792c7c..ed492ba7df4ea041a1b5916a14eb2944a2b69187 100644 --- a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java @@ -171,9 +171,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 39cd91c1e19585d24bbe1333bf15985b0d1a0dab..c4196a7b983efa9a53c1ef2339dc4ee5d6f3bfe2 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 a4dc4154550bb4bd30d3e548f145cff5c693dc18..33ad3583a524bb61cc7116b50e0fc87fa9383e96 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -380,7 +380,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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; @@ -714,6 +714,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S } // 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(); @@ -1181,7 +1188,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S } 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); @@ -1243,7 +1250,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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; @@ -1261,10 +1268,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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) { @@ -1300,6 +1307,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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); } @@ -1341,7 +1354,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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); @@ -1368,8 +1381,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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) { @@ -1405,6 +1418,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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); } @@ -1719,7 +1738,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S } 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 } } @@ -1728,7 +1747,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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); @@ -1844,7 +1866,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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)) @@ -1960,8 +1982,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S 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 21f827678437ea538f0130812c9869e3619041a5..8c34808770e590ea3308d81f891ab1f2b1268739 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; } @@ -190,9 +196,46 @@ 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 public void tick() { @@ -201,9 +244,16 @@ 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); + } + ++this.time; if (!this.isNoGravity()) { - this.addDeltaMovement(0.0D, -0.04D, 0.0D); // Sakura - reduce movement allocations + double gravity = this.physics.before(1_14_0) ? 0.04F : 0.04D; + this.addDeltaMovement(0.0D, -gravity, 0.0D); // Sakura - reduce movement allocations + // Sakura end - physics version api } this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement @@ -217,8 +267,16 @@ 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(); @@ -243,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); @@ -311,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 2db76425a1f295ad179aacfb3abb7efb1be647d9..e7dd0c8aab980d9850a2124598f8a0369603e825 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -60,6 +60,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 @@ -107,12 +114,30 @@ 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 public void tick() { // Sakura - remove max tnt per tick if (!this.isNoGravity()) { - this.addDeltaMovement(0.0D, -0.04D, 0.0D); // Sakura - reduce movement allocations + // Sakura start - physics version api + double gravity = this.physics.before(1_14_0) ? 0.04F : 0.04D; + this.addDeltaMovement(0.0D, -gravity, 0.0D); // Sakura - reduce movement allocations } this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement @@ -122,15 +147,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 +212,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 } @@ -244,7 +276,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 e2a560846ed479309b18d8474b3c9f9d1f89bd5c..c1687a2867832387c9038eb8c7b6b2fcd3d836e1 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -75,6 +75,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)); @@ -113,6 +114,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 @@ -505,8 +507,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 @@ -611,6 +617,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; } @@ -716,9 +730,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; @@ -1050,7 +1070,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 } @@ -1058,6 +1078,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 8e5bd44b93cfd4b3abdb76a5ee6dd6eb4e714223..ea15ef94540b78029c8f78021ace6998f62a701a 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -266,6 +266,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 // 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 7c994b72820cbff158009e86b7d540cb479b01b2..6456e0c20d654b6b001e73bf815da9efd2883a1c 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 public 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 6524272aab5a876e2a2164181da72c765959b550..4c242b501e7e5c7af6676b9554b00405838c4eb0 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, 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 dbfbf477ceef5bd162e1740471f719466f49c1a8..86ed1e1cdbab5004a767831bcb0b34d2b2c3adf8 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 public 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 9b3dcf1a4d4cece92a629506d341f6bfe79d13d0..1ed3568b363df38b196567d8a93089550913d659 100644 --- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java +++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java @@ -192,8 +192,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 93df0a39de85a5fa4b0ab680954405171646915d..f2c393dc0b0ece39932f202b404f2e385acd68ef 100644 --- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -685,6 +685,10 @@ public class RedStoneWireBlock extends Block { public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, 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 05e14322e519d1399e87beb532e1cc4a95f689aa..8b86ec469d7d0a0a7a62d563bd585b5b46815517 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 public 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 54abde54b2341d85c6f02a0a28f02a0e229de42b..4bd60b3f16809bb89212ada6dfc374f0595fec80 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 @@ -111,6 +111,17 @@ public class MovingPistonBlock extends BaseEntityBlock { @Override public 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 be74adc86f0ca467f3b59e7b57fd47a8f381d86e..cd66c83a91ea9ee9f3f48af40889e2d58e0355f5 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 @@ -170,6 +170,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 d62cc6e8075b6c1564df29c1b15e8c0246ae786c..b8c33de8aff6c7df5e4e6b628a06d59e8fec4199 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 public 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 { public 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 c89e1fe5fafac46d82b2b20d27586c6997ce8906..3ae9eea57de59c2d78a719412cdc6559d657ebb7 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 @@ -163,6 +163,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(); @@ -292,14 +298,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)) { @@ -319,12 +478,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 67db57686dd29713128d5b233454b6147b8e460b..e53327f05cb6843c6ef78102042369f762e3677f 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java @@ -527,7 +527,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