diff --git a/migrate/server/feature/0037-Configure-cannon-physics-by-version.patch b/migrate/server/feature/applied/0037-Configure-cannon-physics-by-version.patch similarity index 100% rename from migrate/server/feature/0037-Configure-cannon-physics-by-version.patch rename to migrate/server/feature/applied/0037-Configure-cannon-physics-by-version.patch diff --git a/migrate/server/feature/0042-Allow-explosions-to-destroy-lava.patch b/migrate/server/feature/applied/0042-Allow-explosions-to-destroy-lava.patch similarity index 100% rename from migrate/server/feature/0042-Allow-explosions-to-destroy-lava.patch rename to migrate/server/feature/applied/0042-Allow-explosions-to-destroy-lava.patch diff --git a/migrate/server/feature/0044-Treat-solid-blocks-as-full-when-moving-fast.patch b/migrate/server/feature/applied/0044-Treat-solid-blocks-as-full-when-moving-fast.patch similarity index 100% rename from migrate/server/feature/0044-Treat-solid-blocks-as-full-when-moving-fast.patch rename to migrate/server/feature/applied/0044-Treat-solid-blocks-as-full-when-moving-fast.patch diff --git a/migrate/server/feature/0045-Reduce-entity-tracker-player-updates.patch b/migrate/server/feature/applied/0045-Reduce-entity-tracker-player-updates.patch similarity index 100% rename from migrate/server/feature/0045-Reduce-entity-tracker-player-updates.patch rename to migrate/server/feature/applied/0045-Reduce-entity-tracker-player-updates.patch diff --git a/migrate/server/feature/0046-Add-option-for-legacy-lava-block-formation.patch b/migrate/server/feature/applied/0046-Add-option-for-legacy-lava-block-formation.patch similarity index 100% rename from migrate/server/feature/0046-Add-option-for-legacy-lava-block-formation.patch rename to migrate/server/feature/applied/0046-Add-option-for-legacy-lava-block-formation.patch diff --git a/migrate/server/feature/0057-Add-entity-travel-distance-limits.patch b/migrate/server/feature/applied/0057-Add-entity-travel-distance-limits.patch similarity index 100% rename from migrate/server/feature/0057-Add-entity-travel-distance-limits.patch rename to migrate/server/feature/applied/0057-Add-entity-travel-distance-limits.patch diff --git a/migrate/server/feature/0071-Protect-scaffolding-from-creepers.patch b/migrate/server/feature/applied/0071-Protect-scaffolding-from-creepers.patch similarity index 100% rename from migrate/server/feature/0071-Protect-scaffolding-from-creepers.patch rename to migrate/server/feature/applied/0071-Protect-scaffolding-from-creepers.patch diff --git a/migrate/server/feature/0074-Configurable-left-shooting-and-adjusting-limits.patch b/migrate/server/feature/applied/0074-Configurable-left-shooting-and-adjusting-limits.patch similarity index 100% rename from migrate/server/feature/0074-Configurable-left-shooting-and-adjusting-limits.patch rename to migrate/server/feature/applied/0074-Configurable-left-shooting-and-adjusting-limits.patch diff --git a/migrate/server/feature/0075-Optimise-hopper-ticking.patch b/migrate/server/feature/applied/0075-Optimise-hopper-ticking.patch similarity index 100% rename from migrate/server/feature/0075-Optimise-hopper-ticking.patch rename to migrate/server/feature/applied/0075-Optimise-hopper-ticking.patch diff --git a/migrate/server/feature/0076-Tick-entity-schedulers-only-when-necessary.patch b/migrate/server/feature/applied/0076-Tick-entity-schedulers-only-when-necessary.patch similarity index 100% rename from migrate/server/feature/0076-Tick-entity-schedulers-only-when-necessary.patch rename to migrate/server/feature/applied/0076-Tick-entity-schedulers-only-when-necessary.patch diff --git a/migrate/server/feature/0079-Optimise-check-inside-blocks-and-traverse-blocks.patch b/migrate/server/feature/applied/0079-Optimise-check-inside-blocks-and-traverse-blocks.patch similarity index 100% rename from migrate/server/feature/0079-Optimise-check-inside-blocks-and-traverse-blocks.patch rename to migrate/server/feature/applied/0079-Optimise-check-inside-blocks-and-traverse-blocks.patch diff --git a/sakura-server/minecraft-patches/features/0015-Configure-cannon-physics.patch b/sakura-server/minecraft-patches/features/0015-Configure-cannon-physics.patch new file mode 100644 index 0000000..950351f --- /dev/null +++ b/sakura-server/minecraft-patches/features/0015-Configure-cannon-physics.patch @@ -0,0 +1,1197 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Tue, 21 Nov 2023 17:03:08 +0000 +Subject: [PATCH] Configure cannon physics + + +diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +index 651a45b795818bd7b1364b95c52570fd99dd35e4..47c8ed946cb2ad81a4469daf60dabc40c5e8beda 100644 +--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java ++++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +@@ -1774,6 +1774,13 @@ public final class CollisionUtil { + } + + public static Vec3 performAABBCollisions(final Vec3 moveVector, AABB axisalignedbb, final List potentialCollisions) { ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + double x = moveVector.x; + double y = moveVector.y; + double z = moveVector.z; +@@ -1785,7 +1792,10 @@ public final class CollisionUtil { + } + } + +- final boolean xSmaller = Math.abs(x) < Math.abs(z); ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + + if (xSmaller && z != 0.0) { + z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions); +@@ -1811,9 +1821,18 @@ public final class CollisionUtil { + public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb, + final List voxels, + final List aabbs) { ++ // Sakura start - configure cannon physics ++ 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) { ++ // Sakura end - configure cannon physics + if (voxels.isEmpty()) { + // fast track only AABBs +- return performAABBCollisions(moveVector, axisalignedbb, aabbs); ++ return performAABBCollisions(moveVector, axisalignedbb, aabbs, physics); // Sakura - configure cannon physics + } + + double x = moveVector.x; +@@ -1828,7 +1847,10 @@ public final class CollisionUtil { + } + } + +- final boolean xSmaller = Math.abs(x) < Math.abs(z); ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + + if (xSmaller && z != 0.0) { + z = performAABBCollisionsZ(axisalignedbb, z, aabbs); +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 8c3e0ca06f89e4d8c08d30272475cdeaca20b3ef..a6a47d9d0d8e475d936ce867cdbb8d597e71ff4e 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -580,6 +580,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + // Sakura end - merge cannon entities ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + + public Entity(EntityType entityType, Level level) { + this.type = entityType; +@@ -1092,7 +1099,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 - configure cannon physics + AABB boundingBox = this.getBoundingBox(); + AABB aabb = new AABB(boundingBox.minX, boundingBox.minY - 1.0E-6, boundingBox.minZ, boundingBox.maxX, boundingBox.minY, boundingBox.maxZ); + Optional optional = this.level.findSupportingBlock(this, aabb); +@@ -1147,7 +1154,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) { ++ if (movementType == MoverType.PISTON && this.physics.afterOrEqual(1_11_0)) { // Sakura - configure cannon physics + movement = this.limitPistonMovement(movement); + if (movement.equals(Vec3.ZERO)) { + return; +@@ -1165,8 +1172,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + Vec3 vec3d1 = this.sakura_collide(movement); + double d0 = vec3d1.lengthSqr(); + +- if (d0 > 1.0E-7D || movement.lengthSqr() - d0 < 1.0E-7D) { +- if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock) { ++ if (d0 > 1.0E-7D || this.physics.afterOrEqual(1_21_2) && movement.lengthSqr() - d0 < 1.0E-7D || this.physics.before(1_14_0)) { // Sakura - configure cannon physics ++ if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock && this.physics.afterOrEqual(1_18_2)) { // Sakura - configure cannon physics + BlockHitResult clipResult = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this)); + if (clipResult.getType() != HitResult.Type.MISS) { + this.resetFallDistance(); +@@ -1195,6 +1202,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } else { + if (this.horizontalCollision) { + Vec3 vec3d2 = this.getDeltaMovement(); ++ // Sakura start - configure cannon physics ++ if (movedX && movedZ && this.physics.isWithin(1_14_0, 1_18_1)) { ++ movedX = false; ++ } ++ // Sakura end - configure cannon physics + this.setDeltaMovement(movedX ? 0.0D : vec3d2.x, vec3d2.y, movedZ ? 0.0D : vec3d2.z); + } + +@@ -1586,7 +1598,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); + } + this.collectCollisions(bb, voxelList, bbList); +- return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, voxelList, bbList); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, voxelList, bbList, this.physics); // Sakura - configure cannon physics + } + + private Vec3 collideAxisScan(Vec3 movement, AABB currBoundingBox, List voxelList, List bbList) { +@@ -1594,7 +1606,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + double y = movement.y; + double z = movement.z; + +- boolean xSmaller = Math.abs(x) < Math.abs(z); ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + + if (y != 0.0) { + y = this.scanY(currBoundingBox, y, voxelList, bbList); +@@ -1692,7 +1707,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), null // Sakura - load chunks on movement + ); + potentialCollisionsBB.addAll(entityAABBs); +- final Vec3 collided = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currentBox, potentialCollisionsVoxel, potentialCollisionsBB); ++ final Vec3 collided = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currentBox, potentialCollisionsVoxel, potentialCollisionsBB, this.physics); // Sakura - configure cannon physics + + final boolean collidedX = collided.x != movement.x; + final boolean collidedY = collided.y != movement.y; +@@ -1851,9 +1866,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + for (Entity.Movement movement : movements) { + Vec3 vec3 = movement.from(); + Vec3 vec31 = movement.to(); +- AABB aabb = this.makeBoundingBox(vec31).deflate(1.0E-5F); +- +- for (BlockPos blockPos : BlockGetter.boxTraverseBlocks(vec3, vec31, aabb)) { ++ // Sakura start - configure cannon physics ++ double margin = this.physics.afterOrEqual(1_21_2) ? 1.0E-5f : this.physics.afterOrEqual(1_19_3) ? 1.0E-7 : 0.001; ++ AABB aabb = this.makeBoundingBox(vec31).deflate(margin); ++ final Iterable positions; ++ if (this.physics.afterOrEqual(1_21_2)) { ++ positions = BlockGetter.boxTraverseBlocks(vec3, vec31, aabb); ++ } else { ++ positions = BlockPos.betweenClosed(aabb); ++ } ++ for (BlockPos blockPos : positions) { ++ // Sakura end - configure cannon physics + if (!this.isAlive()) { + return; + } +diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java +index c6836ab9a2789520931d2119aeebeaf2179f27fa..d82e5184515c14a819b70c4d621cc6d93d120cc9 100644 +--- a/net/minecraft/world/entity/item/FallingBlockEntity.java ++++ b/net/minecraft/world/entity/item/FallingBlockEntity.java +@@ -119,6 +119,43 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + return itemEntity; + } + // Sakura end - merge cannon entities ++ // Sakura start - configure cannon physics ++ @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) this.level()).getChunkSource().blockChanged(blockposition); ++ } ++ this.discard(EntityRemoveEvent.Cause.DESPAWN); ++ } ++ } ++ // Sakura end - configure cannon physics + + public FallingBlockEntity(EntityType entityType, Level level) { + super(entityType, level); +@@ -138,6 +175,10 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + this.yo = y; + this.zo = z; + this.setStartPos(this.blockPosition()); ++ // Sakura start - configure cannon physics ++ this.physics = level.localConfig().config(this.blockPosition()).physicsVersion; ++ this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; ++ // Sakura end - configure cannon physics + } + + public static FallingBlockEntity fall(Level level, BlockPos pos, BlockState blockState) { +@@ -151,7 +192,11 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + : blockState + ); + if (!CraftEventFactory.callEntityChangeBlockEvent(fallingBlockEntity, pos, blockState.getFluidState().createLegacyBlock())) return fallingBlockEntity; // CraftBukkit +- level.setBlock(pos, blockState.getFluidState().createLegacyBlock(), 3); ++ // Sakura start - configure cannon physics ++ if (fallingBlockEntity.physics.afterOrEqual(1_18_2)) { ++ level.setBlock(pos, blockState.getFluidState().createLegacyBlock(), 3); ++ } ++ // Sakura end - configure cannon physics + level.addFreshEntity(fallingBlockEntity); + return fallingBlockEntity; + } +@@ -202,7 +247,7 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + + @Override + protected double getDefaultGravity() { +- return 0.04; ++ return this.physics.before(1_14_0) ? 0.04f : 0.04; // Sakura - configure cannon physics + } + + @Override +@@ -211,6 +256,11 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause + } else { + Block block = this.blockState.getBlock(); ++ // Sakura start - configure cannon physics ++ if (this.time == 0 && this.physics.before(1_18_2)) { ++ this.removeBlockOnFall(block); ++ } ++ // Sakura end - configure cannon physics + this.time++; + this.applyGravity(); + this.moveStripped(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement +@@ -225,8 +275,15 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + } + // Paper end - Configurable falling blocks height nerf + this.handlePortal(); ++ // Sakura start - configure cannon physics ++ if (this.physics.before(1_12_0)) { ++ this.setDeltaMovement(this.getDeltaMovement().scale(0.98F)); ++ } + if (this.level() instanceof ServerLevel serverLevel && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) { +- BlockPos blockPos = 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 blockPos = this.physics.before(1_17_0) ? this.patchedBlockPosition() : this.blockPosition(); ++ // Sakura end - configure cannon physics + boolean flag = this.level().sakuraConfig().cannons.sand.concreteSolidifyInWater && this.blockState.getBlock() instanceof ConcretePowderBlock; // Sakura - configure concrete solidifying in water + boolean flag1 = flag && this.level().getFluidState(blockPos).is(FluidTags.WATER); + double d = this.getDeltaMovement().lengthSqr(); +@@ -253,8 +310,11 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + } + } else { + BlockState blockState = this.level().getBlockState(blockPos); +- this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7)); +- if (!blockState.is(Blocks.MOVING_PISTON)) { ++ // Sakura start - configure cannon physics ++ double friction = this.physics.before(1_14_0) ? 0.7f : 0.7; ++ this.setDeltaMovement(this.getDeltaMovement().multiply(friction, -0.5, friction)); ++ if (!blockState.is(Blocks.MOVING_PISTON) && (flag1 || !this.physics.isWithin(1_9_0, 1_12_0) || this.isAbleToStackOnBlock())) { ++ // Sakura end - configure cannon physics + if (!this.cancelDrop) { + boolean canBeReplaced = blockState.canBeReplaced( + new DirectionalPlaceContext(this.level(), blockPos, Direction.DOWN, ItemStack.EMPTY, Direction.UP) +@@ -326,7 +386,12 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + } + } + +- this.setDeltaMovement(this.getDeltaMovement().scale(0.98)); ++ // Sakura start - configure cannon physics ++ if (this.physics.afterOrEqual(1_12_0)) { ++ double drag = this.physics.before(1_14_0) ? 0.98f : 0.98; ++ this.setDeltaMovement(this.getDeltaMovement().scale(drag)); ++ } ++ // Sakura end - configure cannon physics + } + } + +diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java +index 2ee04093d7c8b61a48913bd4c929528e357aa971..e8f69371a5a0732974fe084c482c6d6b89f48151 100644 +--- a/net/minecraft/world/entity/item/PrimedTnt.java ++++ b/net/minecraft/world/entity/item/PrimedTnt.java +@@ -80,6 +80,22 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + this.mergeData.setCount(count); // Sakura - specialised explosions + } + // Sakura end - merge cannon entities ++ // Sakura start - configure cannon physics ++ @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 - configure cannon physics + + public PrimedTnt(EntityType entityType, Level level) { + super(entityType, level); +@@ -105,6 +121,13 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + case Y -> this.setDeltaMovement(this.getDeltaMovement().multiply(0.0, 1.0, 0.0)); + } + // Sakura end - configure cannon mechanics ++ // Sakura start - configure cannon physics ++ this.physics = world.localConfig().config(this.blockPosition()).physicsVersion; ++ this.eyeHeight = this.physics.isLegacy() ? 0.49f : this.eyeHeight; ++ if (this.physics.isLegacy()) { ++ this.setDeltaMovement(this.getDeltaMovement().multiply(0.0, 1.0, 0.0)); ++ } ++ // Sakura end - configure cannon physics + } + + @Override +@@ -140,7 +163,7 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + + @Override + protected double getDefaultGravity() { +- return 0.04; ++ return this.physics.before(1_14_0) ? 0.04f : 0.04; // Sakura - configure cannon physics + } + + @Override +@@ -156,14 +179,18 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + return; + } + // Paper end - Configurable TNT height nerf +- this.setDeltaMovement(this.getDeltaMovement().scale(0.98)); ++ // Sakura start - configure cannon physics ++ double drag = this.physics.before(1_14_0) ? 0.98f : 0.98; ++ this.setDeltaMovement(this.getDeltaMovement().scale(drag)); + if (this.onGround()) { +- this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7)); ++ double friction = this.physics.before(1_14_0) ? 0.7f : 0.7; ++ this.setDeltaMovement(this.getDeltaMovement().multiply(friction, -0.5, friction)); ++ // Sakura end - configure cannon physics + } + + int i = this.getFuse() - 1; + this.setFuse(i); +- if (i <= 0) { ++ if (this.physics.before(1_9_0) ? (i < 0) : (i <= 0)) { // Sakura - configure cannon physics + // 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.respawnEntity(); // Sakura - merge cannon entities +@@ -216,13 +243,14 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + return; + } + // CraftBukkit end ++ double explosionY = this.physics.before(1_10_0) ? this.getY() + (double) 0.49f : this.getY(0.0625D); // Sakura - configure cannon physics + this.level() + .explode( + this, + Explosion.getDefaultDamageSource(this.level(), this), + this.usedPortal ? USED_PORTAL_DAMAGE_CALCULATOR : null, + this.getX(), +- this.getY(0.0625), ++ explosionY, // Sakura - configure cannon physics + this.getZ(), + event.getRadius(), // CraftBukkit + event.getFire(), // CraftBukkit +@@ -310,7 +338,7 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak + // Paper start - Option to prevent TNT from moving in water + @Override + public boolean isPushedByFluid() { +- return !this.level().paperConfig().fixes.preventTntFromMovingInWater && this.level().sakuraConfig().cannons.mechanics.tntFlowsInWater && super.isPushedByFluid(); // Sakura - configure cannon mechanics ++ return !this.level().paperConfig().fixes.preventTntFromMovingInWater && this.level().sakuraConfig().cannons.mechanics.tntFlowsInWater && !this.physics.isLegacy() && super.isPushedByFluid(); // Sakura - physics version api // Sakura - configure cannon mechanics + } + // Paper end - Option to prevent TNT from moving in water + } +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index 19aa5010b019e343d0fb085359eac98bcb5b5efa..eeb37d088cec5b2b8e1ac4bd48b4491eed0822e2 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -837,6 +837,170 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + return chunk != null ? chunk.getNoiseBiome(x, y, z) : this.getUncachedNoiseBiome(x, y, z); + } + // Paper end - optimise random ticking ++ // Sakura start - configure cannon physics ++ public final net.minecraft.world.phys.BlockHitResult.Type clipLegacy(Vec3 from, Vec3 to) { ++ int toX = Mth.floor(to.x); ++ int toY = Mth.floor(to.y); ++ int toZ = Mth.floor(to.z); ++ int fromX = Mth.floor(from.x); ++ int fromY = Mth.floor(from.y); ++ int fromZ = Mth.floor(from.z); ++ ++ BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(fromX, fromY, fromZ); ++ LevelChunk chunk = this.getChunkIfLoaded(fromX >> 4, fromZ >> 4); ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ BlockState blockstate = chunk.getBlockState(blockPos); ++ VoxelShape shape = blockstate.getShape(this, blockPos); ++ for (AABB bb : shape.toAabbs()) { ++ if (this.clip(bb, blockPos, from, to)) { ++ return net.minecraft.world.phys.BlockHitResult.Type.BLOCK; ++ } ++ } ++ ++ for (int steps = 0; steps < 16; ++steps) { ++ if (fromX == toX && fromY == toY && fromZ == toZ) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ boolean moveX = true; ++ boolean moveY = true; ++ boolean moveZ = true; ++ double d0 = 999.0D; ++ double d1 = 999.0D; ++ double d2 = 999.0D; ++ ++ if (toX > fromX) { ++ d0 = (double) fromX + 1.0D; ++ } else if (toX < fromX) { ++ d0 = (double) fromX + 0.0D; ++ } else { ++ moveX = false; ++ } ++ ++ if (toY > fromY) { ++ d1 = (double) fromY + 1.0D; ++ } else if (toY < fromY) { ++ d1 = (double) fromY + 0.0D; ++ } else { ++ moveY = false; ++ } ++ ++ if (toZ > fromZ) { ++ d2 = (double) fromZ + 1.0D; ++ } else if (toZ < fromZ) { ++ d2 = (double) fromZ + 0.0D; ++ } else { ++ moveZ = false; ++ } ++ ++ double d3 = 999.0D; ++ double d4 = 999.0D; ++ double d5 = 999.0D; ++ double d6 = to.x - from.x; ++ double d7 = to.y - from.y; ++ double d8 = to.z - from.z; ++ ++ if (moveX) d3 = (d0 - from.x) / d6; ++ if (moveY) d4 = (d1 - from.y) / d7; ++ if (moveZ) d5 = (d2 - from.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 moveDir; ++ if (d3 < d4 && d3 < d5) { ++ moveDir = toX > fromX ? Direction.WEST : Direction.EAST; ++ from = new Vec3(d0, from.y + d7 * d3, from.z + d8 * d3); ++ } else if (d4 < d5) { ++ moveDir = toY > fromY ? Direction.DOWN : Direction.UP; ++ from = new Vec3(from.x + d6 * d4, d1, from.z + d8 * d4); ++ } else { ++ moveDir = toZ > fromZ ? Direction.NORTH : Direction.SOUTH; ++ from = new Vec3(from.x + d6 * d5, from.y + d7 * d5, d2); ++ } ++ ++ fromX = Mth.floor(from.x) - (moveDir == Direction.EAST ? 1 : 0); ++ fromY = Mth.floor(from.y) - (moveDir == Direction.UP ? 1 : 0); ++ fromZ = Mth.floor(from.z) - (moveDir == Direction.SOUTH ? 1 : 0); ++ blockPos.set(fromX, fromY, fromZ); ++ ++ int chunkX = fromX >> 4; ++ int chunkZ = fromZ >> 4; ++ if (chunkX != chunk.locX || chunkZ != chunk.locZ) { ++ chunk = this.getChunkIfLoaded(chunkX, chunkZ); ++ } ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ blockstate = chunk.getBlockState(blockPos); ++ shape = blockstate.getShape(this, blockPos); ++ for (AABB bb : shape.toAabbs()) { ++ if (this.clip(bb, blockPos, from, to)) { ++ 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 = this.clip(bb.minX, x, vec3d.x); ++ double minYd = this.clip(bb.minY, y, vec3d.y); ++ double minZd = this.clip(bb.minZ, z, vec3d.z); ++ double maxXd = this.clip(bb.maxX, x, vec3d.x); ++ double maxYd = this.clip(bb.maxY, y, vec3d.y); ++ double maxZd = this.clip(bb.maxZ, z, vec3d.z); ++ ++ return this.clipX(vec3d, bb, minXd, y, z) || this.clipY(vec3d, bb, minYd, x, z) || this.clipZ(vec3d, bb, minZd, x, y) ++ || this.clipX(vec3d, bb, maxXd, y, z) || this.clipY(vec3d, bb, maxYd, x, z) || this.clipZ(vec3d, bb, maxZd, x, y); ++ } ++ ++ private double clip(double bound, double axisD, double axisN) { ++ if (axisD * axisD < 1.0000000116860974E-7D) { ++ return -1.0; ++ } ++ return (bound - axisN) / axisD; ++ } ++ ++ private boolean clipX(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(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(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 - configure cannon physics + public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities + public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache + public final me.samsuik.sakura.explosion.durable.DurableBlockManager durabilityManager = new me.samsuik.sakura.explosion.durable.DurableBlockManager(); // Sakura - explosion durable blocks +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index f74aee504fa048a43272a3c3e9d1c6f9fa4a5913..040596183eb6c61369e59112fa928e9c129adb34 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -412,6 +412,7 @@ public class ServerExplosion implements Explosion { + return this.damageCalculator.getBlockExplosionResistance(this, this.level, pos, blockState, fluidState); + } + // Sakura end - explosion durable blocks ++ protected final me.samsuik.sakura.physics.PhysicsVersion physics; // Sakura - configure cannon physics + + public ServerExplosion( + ServerLevel level, +@@ -433,6 +434,7 @@ public class ServerExplosion implements Explosion { + this.damageCalculator = damageCalculator == null ? this.makeDamageCalculator(source) : damageCalculator; + this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit + this.consistentRadius = level.localConfig().config(BlockPos.containing(this.center)).consistentRadius; // Sakura - consistent explosion radius ++ this.physics = source != null ? source.physics() : level.localConfig().config(BlockPos.containing(this.center)).physicsVersion; // Sakura - configure cannon physics + } + + private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { +@@ -462,8 +464,13 @@ public class ServerExplosion implements Explosion { + final float density = entity.level().densityCache.getKnownDensity(vec3); + 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 - configure cannon physics ++ } else if (entity.physics().before(1_14_0)) { ++ hitResult = entity.level().clipLegacy(vec3, explosionVector); + } else { +- hitResult = entity.level().clip(new ClipContext(vec3, explosionVector, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType(); ++ final ClipContext.Block blockContext = entity.physics().afterOrEqual(1_16_0) ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE; ++ hitResult = entity.level().clip(new ClipContext(vec3, explosionVector, blockContext, ClipContext.Fluid.NONE, entity)).getType(); ++ // Sakura end - configure cannon physics + } + if (hitResult == HitResult.Type.MISS) { + // Sakura end - replace density cache +@@ -562,6 +569,14 @@ public class ServerExplosion implements Explosion { + } + + if (cachedBlock.outOfWorld) { ++ // Sakura start - configure cannon physics ++ if (this.physics.before(1_17_0)) { ++ currX += incX; ++ currY += incY; ++ currZ += incZ; ++ continue; ++ } ++ // Sakura end - configure cannon physics + break; + } + final BlockState iblockdata = cachedBlock.blockState; +@@ -657,6 +672,12 @@ public class ServerExplosion implements Explosion { + double d2 = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.center.y; + double d3 = entity.getZ() - this.center.z; + double squareRoot = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3); ++ // Sakura start - configure cannon physics ++ if (this.physics.before(1_17_0)) { ++ d = (float) d; ++ squareRoot = (float) squareRoot; ++ } ++ // Sakura end - configure cannon physics + if (squareRoot != 0.0) { + d1 /= squareRoot; + d2 /= squareRoot; +@@ -950,7 +971,7 @@ public class ServerExplosion implements 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, this.directMappedBlockCache, this.mutablePos); // Paper - collision optimisations ++ blockDensity = this.sakura_getSeenPercent(vec3d, entity); // Sakura - configure cannon physics + this.level.densityCache.putDensity(vec3d, entity, blockDensity); + // Sakura end - replace density cache + } +@@ -958,6 +979,16 @@ public class ServerExplosion implements Explosion { + return blockDensity; + } + ++ // Sakura start - configure cannon physics ++ private float sakura_getSeenPercent(Vec3 vec3d, Entity entity) { ++ if (this.physics.afterOrEqual(1_16_0)) { ++ return this.getSeenFraction(vec3d, entity, this.directMappedBlockCache, this.mutablePos); // Paper - collision optimisations ++ } else { ++ return getSeenPercent(vec3d, entity); ++ } ++ } ++ // Sakura end - configure cannon physics ++ + static class CacheKey { + private final Level world; + private final double posX, posY, posZ; +diff --git a/net/minecraft/world/level/block/FallingBlock.java b/net/minecraft/world/level/block/FallingBlock.java +index 1a787426acb68cf8f9491bdd8f903c7e0c00e29f..d410720b27d97dae2a7bbc4455f227064759001a 100644 +--- a/net/minecraft/world/level/block/FallingBlock.java ++++ b/net/minecraft/world/level/block/FallingBlock.java +@@ -45,6 +45,15 @@ public abstract class FallingBlock extends Block implements Fallable { + return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); + } + ++ // Sakura start - configure cannon physics ++ @Override ++ public void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, net.minecraft.world.level.redstone.Orientation wireOrientation, boolean notify) { ++ if (world.localConfig().config(pos).physicsVersion.before(1_18_2)) { ++ world.scheduleTick(pos, this, this.getDelayAfterPlace()); ++ } ++ } ++ // Sakura end - configure cannon physics ++ + @Override + protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (isFree(level.getBlockState(pos.below())) && pos.getY() >= level.getMinY()) { +diff --git a/net/minecraft/world/level/block/FenceGateBlock.java b/net/minecraft/world/level/block/FenceGateBlock.java +index 438f1492ab5b8c7bb2a32828c83e8178c2e04712..81f94bf9351355640c1ffd214638035c6582d87a 100644 +--- a/net/minecraft/world/level/block/FenceGateBlock.java ++++ b/net/minecraft/world/level/block/FenceGateBlock.java +@@ -224,8 +224,14 @@ public class FenceGateBlock extends HorizontalDirectionalBlock { + hasNeighborSignal = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end +- if (state.getValue(POWERED) != hasNeighborSignal) { +- level.setBlock(pos, state.setValue(POWERED, Boolean.valueOf(hasNeighborSignal)).setValue(OPEN, Boolean.valueOf(hasNeighborSignal)), 2); ++ // Sakura start - configure cannon physics ++ final boolean legacy = level.localConfig().config(pos).physicsVersion.before(1_11_0); ++ final boolean powered = state.getValue(POWERED); ++ if (legacy ? (hasNeighborSignal || neighborBlock.defaultBlockState().isSignalSource()) : powered != hasNeighborSignal) { ++ final boolean openGate = legacy && (hasNeighborSignal == powered || state.getValue(OPEN) != powered) ++ ? state.getValue(OPEN) : hasNeighborSignal; ++ level.setBlock(pos, (state.setValue(POWERED, hasNeighborSignal)).setValue(OPEN, openGate), 2); ++ // Sakura end - configure cannon physics + if (state.getValue(OPEN) != hasNeighborSignal) { + level.playSound( + null, +diff --git a/net/minecraft/world/level/block/HoneyBlock.java b/net/minecraft/world/level/block/HoneyBlock.java +index bab3ac2c4be08ea7589752b8472c1e13bcaab76a..fdf8306a569edd074d0debeb07be67da9ada5e77 100644 +--- a/net/minecraft/world/level/block/HoneyBlock.java ++++ b/net/minecraft/world/level/block/HoneyBlock.java +@@ -70,11 +70,19 @@ public class HoneyBlock extends HalfTransparentBlock { + super.entityInside(state, level, pos, entity); + } + +- private static double getOldDeltaY(double deltaY) { ++ // Sakura start - configure cannon physics ++ private static double getOldDeltaY(double deltaY, Entity entity) { ++ if (entity.physics().before(1_21_2)) { ++ return deltaY; ++ } + return deltaY / 0.98F + 0.08; + } + +- private static double getNewDeltaY(double deltaY) { ++ private static double getNewDeltaY(double deltaY, Entity entity) { ++ if (entity.physics().before(1_21_2)) { ++ return deltaY; ++ } ++ // Sakura end - configure cannon physics + return (deltaY - 0.08) * 0.98F; + } + +@@ -83,7 +91,7 @@ public class HoneyBlock extends HalfTransparentBlock { + return false; + } else if (entity.getY() > pos.getY() + 0.9375 - 1.0E-7) { + return false; +- } else if (getOldDeltaY(entity.getDeltaMovement().y) >= -0.08) { ++ } else if (getOldDeltaY(entity.getDeltaMovement().y, entity) >= -0.08) { // Sakura - configure cannon physics + return false; + } else { + double abs = Math.abs(pos.getX() + 0.5 - entity.getX()); +@@ -101,11 +109,13 @@ public class HoneyBlock extends HalfTransparentBlock { + + private void doSlideMovement(Entity entity) { + Vec3 deltaMovement = entity.getDeltaMovement(); +- if (getOldDeltaY(entity.getDeltaMovement().y) < -0.13) { +- double d = -0.05 / getOldDeltaY(entity.getDeltaMovement().y); +- entity.setDeltaMovement(new Vec3(deltaMovement.x * d, getNewDeltaY(-0.05), deltaMovement.z * d)); ++ // Sakura start - configure cannon physics ++ if (getOldDeltaY(entity.getDeltaMovement().y, entity) < -0.13) { ++ double d = -0.05 / getOldDeltaY(entity.getDeltaMovement().y, entity); ++ entity.setDeltaMovement(new Vec3(deltaMovement.x * d, getNewDeltaY(-0.05, entity), deltaMovement.z * d)); + } else { +- entity.setDeltaMovement(new Vec3(deltaMovement.x, getNewDeltaY(-0.05), deltaMovement.z)); ++ entity.setDeltaMovement(new Vec3(deltaMovement.x, getNewDeltaY(-0.05, entity), deltaMovement.z)); ++ // Sakura end - configure cannon physics + } + + entity.resetFallDistance(); +diff --git a/net/minecraft/world/level/block/LadderBlock.java b/net/minecraft/world/level/block/LadderBlock.java +index 352aa47ba6d4e434bcf2af8e70c5abcb734056eb..c615d4fa56d9237f9b71800ee77f6be219ce18c7 100644 +--- a/net/minecraft/world/level/block/LadderBlock.java ++++ b/net/minecraft/world/level/block/LadderBlock.java +@@ -35,6 +35,21 @@ public class LadderBlock extends Block implements SimpleWaterloggedBlock { + private static final VoxelShape SOUTH_AABB_COPY = SOUTH_AABB.copy(); + private static final VoxelShape NORTH_AABB_COPY = NORTH_AABB.copy(); + // Sakura end - protect block shapes against plugins ++ // Sakura start - configure cannon physics ++ 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 - configure cannon physics + + @Override + public MapCodec codec() { +@@ -46,8 +61,18 @@ public class LadderBlock extends Block implements SimpleWaterloggedBlock { + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(WATERLOGGED, Boolean.valueOf(false))); + } + ++ // Sakura start - configure cannon physics ++ @Override ++ public final boolean hasDynamicShape() { ++ return true; ++ } ++ + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { ++ if (level instanceof net.minecraft.world.level.Level gameLevel && gameLevel.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ return legacyShape(state.getValue(FACING)); ++ } ++ // Sakura end - configure cannon physics + switch ((Direction)state.getValue(FACING)) { + case NORTH: + // Sakura start - protect block shapes against plugins +diff --git a/net/minecraft/world/level/block/LiquidBlock.java b/net/minecraft/world/level/block/LiquidBlock.java +index 78aa0ae9ce937b7232eac1d65fd987c21489979d..e719be50ff73610046696a21053671332951ca9c 100644 +--- a/net/minecraft/world/level/block/LiquidBlock.java ++++ b/net/minecraft/world/level/block/LiquidBlock.java +@@ -190,7 +190,20 @@ public class LiquidBlock extends Block implements BucketPickup { + for (Direction direction : POSSIBLE_FLOW_DIRECTIONS) { + BlockPos blockPos = pos.relative(direction.getOpposite()); + if (level.getFluidState(blockPos).is(FluidTags.WATER)) { +- Block block = level.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; ++ // Sakura start - configure cannon physics ++ final FluidState fluidState = state.getFluidState(); ++ final Block block = fluidState.isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; ++ if (block == Blocks.COBBLESTONE) { ++ final me.samsuik.sakura.physics.PhysicsVersion physics = level.localConfig().config(pos).physicsVersion; ++ ++ // SANITY: In legacy a patch by paper removes the fluid level condition from vanilla. ++ if (physics.before(1_16_0) && !physics.isLegacy() && ++ (physics.before(1_13_0) || !(fluidState.getHeight(level, pos) >= 0.44444445f)) && ++ (physics.afterOrEqual(1_13_0) || FlowingFluid.getLegacyLevel(fluidState) > 4)) { ++ return true; ++ } ++ } ++ // Sakura end - configure cannon physics + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState())) { + this.fizz(level, pos); +diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java +index efdcee032174af1f9a14183e9e8af3e7c4694942..c3b72708466bb28a764b0cfc958ffa10863e1a75 100644 +--- a/net/minecraft/world/level/block/RedStoneWireBlock.java ++++ b/net/minecraft/world/level/block/RedStoneWireBlock.java +@@ -572,6 +572,10 @@ public class RedStoneWireBlock extends Block { + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (!player.getAbilities().mayBuild) { + return InteractionResult.PASS; ++ // Sakura start - configure cannon physics ++ } else if (world.localConfig().config(pos).physicsVersion.before(1_16_0)) { ++ return InteractionResult.PASS; ++ // Sakura end - configure cannon physics + } else { + if (isCross(state) || isDot(state)) { + BlockState blockState = isCross(state) ? this.defaultBlockState() : this.crossState; +diff --git a/net/minecraft/world/level/block/WaterlilyBlock.java b/net/minecraft/world/level/block/WaterlilyBlock.java +index f085671bcef6dd9758240f91dfe7c58ef6212f0b..1209bc07200ea1c1752e578363fee9d5d3f542b2 100644 +--- a/net/minecraft/world/level/block/WaterlilyBlock.java ++++ b/net/minecraft/world/level/block/WaterlilyBlock.java +@@ -17,6 +17,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; + public class WaterlilyBlock extends BushBlock { + public static final MapCodec CODEC = simpleCodec(WaterlilyBlock::new); + protected static final VoxelShape AABB = Block.box(1.0, 0.0, 1.0, 15.0, 1.5, 15.0); ++ protected static final VoxelShape LEGACY_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 0.25D, 16.0D); // Sakura - configure cannon physics + + @Override + public MapCodec codec() { +@@ -41,8 +42,18 @@ public class WaterlilyBlock extends BushBlock { + } + } + ++ // Sakura start - configure cannon physics ++ @Override ++ public final boolean hasDynamicShape() { ++ return true; ++ } ++ + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { ++ if (level instanceof net.minecraft.world.level.Level gameLevel && gameLevel.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ return LEGACY_AABB; ++ } ++ // Sakura end - configure cannon physics + return AABB; + } + +diff --git a/net/minecraft/world/level/block/piston/MovingPistonBlock.java b/net/minecraft/world/level/block/piston/MovingPistonBlock.java +index f14e84e67746208dd188525fb91ab30b190d332b..1d66e5848a7f1b3c5e6b70f2e38351667f62ee25 100644 +--- a/net/minecraft/world/level/block/piston/MovingPistonBlock.java ++++ b/net/minecraft/world/level/block/piston/MovingPistonBlock.java +@@ -110,6 +110,16 @@ public class MovingPistonBlock extends BaseEntityBlock { + @Override + protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + PistonMovingBlockEntity blockEntity = this.getBlockEntity(level, pos); ++ // Sakura start - configure cannon physics ++ if (blockEntity != null && level instanceof Level gameLevel && gameLevel.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ final VoxelShape shape = blockEntity.getCollisionShapeFromProgress(level, pos); ++ if (context.isAbove(shape, pos, false)) { ++ return shape; ++ } else { ++ return blockEntity.getMovedState().getCollisionShape(level, pos); ++ } ++ } ++ // Sakura end - configure cannon physics + return blockEntity != null ? blockEntity.getCollisionShape(level, pos) : Shapes.empty(); + } + +diff --git a/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +index ca22b585ef05fef2473bab5387b739f0ec358aad..7a8c7ff8a9c5e77ccb8b914694d02cdafb4be69d 100644 +--- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java ++++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +@@ -145,6 +145,11 @@ public class PistonBaseBlock extends DirectionalBlock { + i = 2; + } + ++ // Sakura start - configure cannon physics ++ if (level.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ level.setBlock(pos, state.setValue(PistonBaseBlock.EXTENDED, false), 18); ++ } ++ // Sakura end - configure cannon physics + level.blockEvent(pos, this, i, direction.get3DDataValue()); + } + } +diff --git a/net/minecraft/world/level/block/piston/PistonHeadBlock.java b/net/minecraft/world/level/block/piston/PistonHeadBlock.java +index a89a42a3ad6cecd5cc4d44e4456d52a2997ba2e5..9c3c0305aa96b4a1b40841d1c1f145af8e86af40 100644 +--- a/net/minecraft/world/level/block/piston/PistonHeadBlock.java ++++ b/net/minecraft/world/level/block/piston/PistonHeadBlock.java +@@ -151,6 +151,11 @@ public class PistonHeadBlock extends DirectionalBlock { + @Override + protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos.relative(state.getValue(FACING).getOpposite())); ++ // Sakura start - configure cannon physics ++ if (level instanceof Level gameLevel && gameLevel.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ return this.isFittingBase(state, blockState); ++ } ++ // Sakura end - configure cannon physics + return this.isFittingBase(state, blockState) || blockState.is(Blocks.MOVING_PISTON) && blockState.getValue(FACING) == state.getValue(FACING); + } + +@@ -162,6 +167,10 @@ public class PistonHeadBlock extends DirectionalBlock { + neighborBlock, + ExperimentalRedstoneUtils.withFront(orientation, state.getValue(FACING).getOpposite()) + ); ++ // Sakura start - configure cannon physics ++ } else if (level.localConfig().config(pos).physicsVersion.before(1_9_0)) { ++ level.setBlock(pos, Blocks.AIR.defaultBlockState(), 19); ++ // Sakura end - configure cannon physics + } + } + +diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +index a6413663112685554afd79f13d3d76f1cd753801..184a9c681b4d1359d028d227dc0a74d7b78b7667 100644 +--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java ++++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +@@ -165,6 +165,12 @@ public class PistonMovingBlockEntity extends BlockEntity { + + double d4 = 0.0; + ++ // Sakura start - configure cannon physics ++ if (entity.physics().before(1_11_0)) { ++ moveEntityByPistonFromDirection(movementDirection, entity, aabb); ++ return; ++ } ++ // Sakura end - configure cannon physics + for (AABB aabb1 : list) { + AABB movementArea = PistonMath.getMovementArea(moveByPositionAndProgress(pos, aabb1, piston), movementDirection, d); + AABB boundingBox = entity.getBoundingBox(); +@@ -292,17 +298,170 @@ public class PistonMovingBlockEntity extends BlockEntity { + } + } + ++ // Sakura start - configure cannon physics ++ @javax.annotation.Nullable ++ private AABB getBoundsFromProgress(BlockGetter 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(BlockGetter 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 - configure cannon physics ++ + public Direction getPushDirection() { + return this.extending ? this.direction : this.direction.getOpposite(); + } + + public static void tick(Level level, BlockPos pos, BlockState state, PistonMovingBlockEntity blockEntity) { ++ final me.samsuik.sakura.physics.PhysicsVersion physics = level.localConfig().config(pos).physicsVersion; // Sakura - configure cannon physics + blockEntity.lastTicked = level.getGameTime(); + blockEntity.progressO = blockEntity.progress; + if (blockEntity.progressO >= 1.0F) { + if (level.isClientSide && blockEntity.deathTicks < 5) { + blockEntity.deathTicks++; + } else { ++ // Sakura start - configure cannon physics ++ if (physics.isWithin(1_9_0, 1_10_0)) { ++ moveCollidedEntities(level, pos, 1.0f, blockEntity); ++ moveStuckEntities(level, pos, 1.0f, blockEntity); ++ } else if (physics.before(1_9_0)) { ++ blockEntity.moveEntities(level, 0.25f); ++ } ++ // Sakura end - configure cannon physics + level.removeBlockEntity(pos); + blockEntity.setRemoved(); + if (level.getBlockState(pos).is(Blocks.MOVING_PISTON)) { +@@ -324,12 +483,22 @@ public class PistonMovingBlockEntity extends BlockEntity { + } + } else { + float f = blockEntity.progress + 0.5F; ++ // Sakura start - physics version api ++ if (physics.afterOrEqual(1_11_0)) { + moveCollidedEntities(level, pos, f, blockEntity); + moveStuckEntities(level, pos, f, blockEntity); ++ } + blockEntity.progress = f; + if (blockEntity.progress >= 1.0F) { + blockEntity.progress = 1.0F; + } ++ if (physics.isWithin(1_9_0, 1_10_0)) { ++ moveCollidedEntities(level, pos, f, blockEntity); ++ moveStuckEntities(level, pos, f, blockEntity); ++ } else if (blockEntity.extending && physics.before(1_9_0)) { ++ blockEntity.moveEntities(level, blockEntity.progress - blockEntity.progressO + 0.0625f); ++ } ++ // Sakura end - physics version api + } + } + +diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java +index 3ee197e533460ef4fa87bebe18d02ffc76de8550..9b714ddad7208dd9509bf9f8434f1acea5a6f213 100644 +--- a/net/minecraft/world/level/material/LavaFluid.java ++++ b/net/minecraft/world/level/material/LavaFluid.java +@@ -172,7 +172,10 @@ public abstract class LavaFluid extends FlowingFluid { + + @Override + public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) { +- return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER); ++ // Sakura start - configure cannon physics ++ return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER) ++ && blockReader instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); ++ // Sakura end - configure cannon physics + } + + @Override +diff --git a/net/minecraft/world/level/material/WaterFluid.java b/net/minecraft/world/level/material/WaterFluid.java +index 56781b47aeddf0c84d64ddf8b1aad7b26730b68c..6f478324e9edaf52f8938fb6e24208495463454f 100644 +--- a/net/minecraft/world/level/material/WaterFluid.java ++++ b/net/minecraft/world/level/material/WaterFluid.java +@@ -114,7 +114,12 @@ public abstract class WaterFluid extends FlowingFluid { + + @Override + public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) { +- return direction == Direction.DOWN && !fluid.is(FluidTags.WATER); ++ // Sakura start - configure cannon physics ++ if (direction == Direction.DOWN && !fluid.is(FluidTags.WATER) || !(blockReader instanceof Level level)) { ++ return true; ++ } ++ return fluid.is(FluidTags.LAVA) && level.localConfig().config(pos).physicsVersion.before(1_13_0); ++ // Sakura end - configure cannon physics + } + + @Override diff --git a/sakura-server/minecraft-patches/features/0016-Allow-explosions-to-destroy-lava.patch b/sakura-server/minecraft-patches/features/0016-Allow-explosions-to-destroy-lava.patch new file mode 100644 index 0000000..0b3f5d8 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0016-Allow-explosions-to-destroy-lava.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Sat, 25 Nov 2023 21:14:45 +0000 +Subject: [PATCH] Allow explosions to destroy lava + + +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index 040596183eb6c61369e59112fa928e9c129adb34..ad44fb3a3ee0b6595564031f73fc61c459a8d951 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -407,6 +407,11 @@ public class ServerExplosion implements Explosion { + return Optional.of(ZERO_RESISTANCE); + } + // Sakura end - destroy water logged blocks ++ // Sakura start - allow explosions to destroy lava ++ if (blockState.is(Blocks.LAVA) && this.level.sakuraConfig().cannons.explosion.explodeLava) { ++ return Optional.of(ZERO_RESISTANCE); ++ } ++ // Sakura end - allow explosions to destroy lava + } + + return this.damageCalculator.getBlockExplosionResistance(this, this.level, pos, blockState, fluidState); +diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java +index 5473b4006f7e0266ea11a7b05cef78a113c30d97..0a233c18fbad92c59b9c001574be3464f2be9d2c 100644 +--- a/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -204,7 +204,7 @@ public abstract class BlockBehaviour implements FeatureElement { + state.getDrops(builder).forEach(stack -> dropConsumer.accept(stack, pos)); + } + +- level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3); ++ level.setBlock(pos, Blocks.AIR.defaultBlockState(), level.sakuraConfig().cannons.explosion.explodeLava && state.is(Blocks.LAVA) ? 2 : 3); // Sakura - allow explosions to destroy lava; don't cause block updates when blowing up lava + block.wasExploded(level, pos, explosion); + } + } diff --git a/sakura-server/minecraft-patches/features/0017-Collide-with-non-solid-blocks.patch b/sakura-server/minecraft-patches/features/0017-Collide-with-non-solid-blocks.patch new file mode 100644 index 0000000..a2d006d --- /dev/null +++ b/sakura-server/minecraft-patches/features/0017-Collide-with-non-solid-blocks.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Sun, 26 Nov 2023 17:57:50 +0000 +Subject: [PATCH] Collide with non-solid blocks + + +diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +index 47c8ed946cb2ad81a4469daf60dabc40c5e8beda..16b66b19157081c7717f73ee3dc9111662a31922 100644 +--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java ++++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +@@ -1908,6 +1908,7 @@ public final class CollisionUtil { + public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; + public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; + public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura - load chunks on movement ++ public static final int COLLISION_FLAG_FULL_BLOCKS = 1 << 5; // Sakura - collide with non-solid blocks + + public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb, + final List intoVoxel, final List intoAABB, +@@ -1960,6 +1961,7 @@ public final class CollisionUtil { + + final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; + final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura - load chunks on movement ++ final boolean fullBlocks = (collisionFlags & COLLISION_FLAG_FULL_BLOCKS) != 0; // Sakura - collide with non-solid blocks + final ChunkSource chunkSource = world.getChunkSource(); + + for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { +@@ -1999,7 +2001,7 @@ public final class CollisionUtil { + continue; + } + +- final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks(); ++ final boolean hasSpecial = !fullBlocks && ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks(); // Sakura - collide with non-solid blocks + final int sectionAdjust = !hasSpecial ? 1 : 0; + + final PalettedContainer blocks = section.states; +@@ -2038,6 +2040,11 @@ public final class CollisionUtil { + if (useEntityCollisionShape) { + mutablePos.set(blockX, blockY, blockZ); + blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos); ++ // Sakura start - collide with non-solid blocks ++ // todo: move this logic above emptyCollisionShape and consider all blocks as a solid except scaffolding and liquids ++ } else if (fullBlocks && blockData.moonrise$getConstantContextCollisionShape() != null) { ++ blockCollision = net.minecraft.world.phys.shapes.Shapes.block(); ++ // Sakura end - collide with non-solid blocks + } else if (blockCollision == null) { + mutablePos.set(blockX, blockY, blockZ); + blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index a6a47d9d0d8e475d936ce867cdbb8d597e71ff4e..7c3b536ee056cfd938f19a0a22b1d128f0fa2733 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -541,6 +541,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET; + } + ++ // Sakura start - collide with non-solid blocks ++ if (this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMoving && (this.isPrimedTNT || this.isFallingBlock)) { ++ double horizontalMovementSqr = this.getDeltaMovement().horizontalDistanceSqr(); ++ if (horizontalMovementSqr > Math.pow(this.level().sakuraConfig().cannons.treatAllBlocksAsFullWhenMovingFasterThan, 2.0)) { ++ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_FULL_BLOCKS; ++ } ++ } ++ // Sakura end - collide with non-solid blocks + return flags; + } + // Sakura end - load chunks on movement diff --git a/sakura-server/minecraft-patches/features/0018-Reduce-entity-tracker-player-updates.patch b/sakura-server/minecraft-patches/features/0018-Reduce-entity-tracker-player-updates.patch new file mode 100644 index 0000000..1ebe078 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0018-Reduce-entity-tracker-player-updates.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Thu, 30 Nov 2023 15:54:49 +0000 +Subject: [PATCH] Reduce entity tracker player updates + + +diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java +index e096463443639e9eef5311d7154f6d2ac1517883..ec166e88f0257890eb87efa3ec9c5fd6725d3569 100644 +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -953,7 +953,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (tracker == null) { + continue; + } +- ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers); ++ // Sakura start - reduce entity tracker player updates ++ if (tracker.shouldUpdatePlayers()) { ++ ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers); ++ } ++ // Sakura end - reduce entity tracker player updates + if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$hasPlayers() + || ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) { + tracker.serverEntity.sendChanges(); +@@ -1197,12 +1201,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + return state != me.samsuik.sakura.player.visibility.VisibilityState.OFF; + } + // Sakura end - client visibility settings; entity visibility ++ // Sakura start - reduce entity tracker player updates ++ private final int playerUpdateInterval; ++ private net.minecraft.world.phys.Vec3 entityPosition; ++ ++ public final boolean shouldUpdatePlayers() { ++ // We have to always update players otherwise they can turn invisible on teleports (why?) ++ if (this.entity instanceof net.minecraft.world.entity.player.Player || this.entity.tickCount % this.playerUpdateInterval == 0) { ++ return true; ++ } ++ final net.minecraft.world.phys.Vec3 lastPosition = this.entityPosition; ++ this.entityPosition = this.entity.position(); ++ return this.entity.position().distanceToSqr(lastPosition) >= (double) this.range / 2.0; ++ } ++ // Sakura start - reduce entity tracker player updates + + public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast, this.seenBy); // CraftBukkit + this.entity = entity; + this.range = range; + this.lastSectionPos = SectionPos.of(entity); ++ // Sakura end - client visibility settings; entity visibility ++ // Sakura start - reduce entity tracker player updates ++ this.playerUpdateInterval = Math.min(j, 20); ++ this.entityPosition = entity.position(); ++ // Sakura start - reduce entity tracker player updates + } + + @Override diff --git a/sakura-server/minecraft-patches/features/0019-Legacy-lava-block-formation.patch b/sakura-server/minecraft-patches/features/0019-Legacy-lava-block-formation.patch new file mode 100644 index 0000000..854c9b9 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0019-Legacy-lava-block-formation.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Sat, 2 Dec 2023 15:14:15 +0000 +Subject: [PATCH] Legacy lava block formation + + +diff --git a/net/minecraft/world/level/block/LiquidBlock.java b/net/minecraft/world/level/block/LiquidBlock.java +index e719be50ff73610046696a21053671332951ca9c..9f8f71c95086b18be4d9254ca8aa7f93674fb598 100644 +--- a/net/minecraft/world/level/block/LiquidBlock.java ++++ b/net/minecraft/world/level/block/LiquidBlock.java +@@ -194,7 +194,14 @@ public class LiquidBlock extends Block implements BucketPickup { + final FluidState fluidState = state.getFluidState(); + final Block block = fluidState.isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; + if (block == Blocks.COBBLESTONE) { +- final me.samsuik.sakura.physics.PhysicsVersion physics = level.localConfig().config(pos).physicsVersion; ++ // Sakura start - legacy lava block formation ++ final me.samsuik.sakura.physics.PhysicsVersion physics; ++ if (world.sakuraConfig().environment.blockGeneration.legacyBlockFormation) { ++ physics = me.samsuik.sakura.physics.PhysicsVersion.v1_12; ++ } else { ++ physics = level.localConfig().config(pos).physicsVersion; ++ } ++ // Sakura end - legacy lava block formation + + // SANITY: In legacy a patch by paper removes the fluid level condition from vanilla. + if (physics.before(1_16_0) && !physics.isLegacy() && +diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java +index 9b714ddad7208dd9509bf9f8434f1acea5a6f213..d073864e6d4be4e8001ff6e6a0a1dd92e95a9323 100644 +--- a/net/minecraft/world/level/material/LavaFluid.java ++++ b/net/minecraft/world/level/material/LavaFluid.java +@@ -174,7 +174,8 @@ public abstract class LavaFluid extends FlowingFluid { + public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) { + // Sakura start - configure cannon physics + return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER) +- && blockReader instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0); ++ && blockReader instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0) ++ && !level.sakuraConfig().environment.blockGeneration.legacyBlockFormation; // Sakura - legacy lava block formation + // Sakura end - configure cannon physics + } + +diff --git a/net/minecraft/world/level/material/WaterFluid.java b/net/minecraft/world/level/material/WaterFluid.java +index 6f478324e9edaf52f8938fb6e24208495463454f..a1677a683dc530c0af5f2ead275cf57482860737 100644 +--- a/net/minecraft/world/level/material/WaterFluid.java ++++ b/net/minecraft/world/level/material/WaterFluid.java +@@ -118,7 +118,10 @@ public abstract class WaterFluid extends FlowingFluid { + if (direction == Direction.DOWN && !fluid.is(FluidTags.WATER) || !(blockReader instanceof Level level)) { + return true; + } +- return fluid.is(FluidTags.LAVA) && level.localConfig().config(pos).physicsVersion.before(1_13_0); ++ // Sakura start - legacy lava block formation ++ return fluid.is(FluidTags.LAVA) && (level.localConfig().config(pos).physicsVersion.before(1_13_0) ++ || level.sakuraConfig().environment.blockGeneration.legacyBlockFormation); ++ // Sakura end - legacy lava block formation + // Sakura end - configure cannon physics + } + diff --git a/sakura-server/minecraft-patches/features/0020-Add-entity-travel-distance-limits.patch b/sakura-server/minecraft-patches/features/0020-Add-entity-travel-distance-limits.patch new file mode 100644 index 0000000..b42ed0a --- /dev/null +++ b/sakura-server/minecraft-patches/features/0020-Add-entity-travel-distance-limits.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Tue, 20 Feb 2024 19:16:16 +0000 +Subject: [PATCH] Add entity travel distance limits + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index ee44ba44773f245d351aac9461bd6cff18204f01..12f5f0002d2b298c4f9bd021d13626ddb5719317 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1292,6 +1292,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + final boolean isActive = io.papermc.paper.entity.activation.ActivationRange.checkIfActive(entity); // Paper - EAR 2 + if (isActive) { // Paper - EAR 2 + entity.tick(); ++ // Sakura start - entity travel distance limits ++ if (entity.isPastTravelDistanceLimit()) { ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); ++ } ++ // Sakura end - entity travel distance limits + entity.postTick(); // CraftBukkit + } else {entity.inactiveTick();} // Paper - EAR 2 + profilerFiller.pop(); +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 7c3b536ee056cfd938f19a0a22b1d128f0fa2733..37d2ab5f3cfe10478e87d1649d072fe6b69fb914 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -595,6 +595,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return this.physics; + } + // Sakura end - configure cannon physics ++ // Sakura start - entity travel distance limits ++ private final double travelDistanceLimit; ++ ++ public final boolean isPastTravelDistanceLimit() { ++ if (this.origin == null) { ++ return false; ++ } ++ ++ double x = Math.pow(this.origin.getX() - this.position.x(), 2); ++ double z = Math.pow(this.origin.getZ() - this.position.z(), 2); ++ return Math.max(x, z) >= this.travelDistanceLimit; ++ } ++ // Sakura end - entity travel distance limits + + public Entity(EntityType entityType, Level level) { + this.type = entityType; +@@ -624,6 +637,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.setPos(0.0, 0.0, 0.0); + this.eyeHeight = this.dimensions.eyeHeight(); + this.despawnTime = type == EntityType.PLAYER ? -1 : level.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit ++ this.travelDistanceLimit = Math.pow(this.level.sakuraConfig().entity.chunkTravelLimit.getOrDefault(this.type, Integer.MAX_VALUE) * 16.0, 2); // Sakura - entity travel distance limits + } + + public boolean isColliding(BlockPos pos, BlockState state) { diff --git a/sakura-server/minecraft-patches/features/0021-Protect-scaffolding-from-creepers.patch b/sakura-server/minecraft-patches/features/0021-Protect-scaffolding-from-creepers.patch new file mode 100644 index 0000000..82a8cb9 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0021-Protect-scaffolding-from-creepers.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Mon, 17 Jun 2024 14:04:12 +0100 +Subject: [PATCH] Protect scaffolding from creepers + + +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index ad44fb3a3ee0b6595564031f73fc61c459a8d951..d2d2bb0e60f4f12761fa02b1c6fb293d15e4efed 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -412,6 +412,11 @@ public class ServerExplosion implements Explosion { + return Optional.of(ZERO_RESISTANCE); + } + // Sakura end - allow explosions to destroy lava ++ // Sakura start - protect scaffolding from creepers ++ if (this.level.sakuraConfig().cannons.explosion.protectScaffoldingFromCreepers && blockState.is(Blocks.SCAFFOLDING) && this.source instanceof net.minecraft.world.entity.monster.Creeper) { ++ return Optional.of(Blocks.BARRIER.getExplosionResistance()); ++ } ++ // Sakura end - protect scaffolding from creepers + } + + return this.damageCalculator.getBlockExplosionResistance(this, this.level, pos, blockState, fluidState); diff --git a/sakura-server/minecraft-patches/features/0022-Configurable-left-shooting-and-adjusting-limits.patch b/sakura-server/minecraft-patches/features/0022-Configurable-left-shooting-and-adjusting-limits.patch new file mode 100644 index 0000000..f20f8fb --- /dev/null +++ b/sakura-server/minecraft-patches/features/0022-Configurable-left-shooting-and-adjusting-limits.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 9 Aug 2024 20:43:53 +0100 +Subject: [PATCH] Configurable left shooting and adjusting limits + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 37d2ab5f3cfe10478e87d1649d072fe6b69fb914..f9b9d81cc60785488a7cf57caabb4a5fdd84ecae 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -608,6 +608,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return Math.max(x, z) >= this.travelDistanceLimit; + } + // Sakura end - entity travel distance limits ++ // Sakura start - configurable left shooting and adjusting limits ++ public final void limitLeftShooting() { ++ Vec3 movement = this.getDeltaMovement(); ++ int threshold = this.level.sakuraConfig().cannons.restrictions.leftShootingThreshold.or(-1); ++ if (threshold > 0 && (movement.x != 0.0 || movement.z != 0.0) && this.origin != null) { ++ double travelledX = Math.abs(this.getX() - this.origin.getX()); ++ double travelledZ = Math.abs(this.getZ() - this.origin.getZ()); ++ boolean xSmaller = travelledX < travelledZ; // intended ++ ++ // Once entities have travelled past the threshold changing direction is restricted. ++ if (xSmaller && travelledX > threshold) { ++ this.setDeltaMovement(movement.multiply(1.0, 1.0, 0.0)); // limit z ++ } else if (!xSmaller && travelledZ > threshold) { ++ this.setDeltaMovement(movement.multiply(0.0, 1.0, 1.0)); // limit x ++ } ++ } ++ } ++ ++ public final void limitAdjustMovement(AABB currBoundingBox, double dir, boolean xAdjust, List shapes) { ++ int adjustDistance = this.level.sakuraConfig().cannons.restrictions.maxAdjustDistance.or(-1); ++ if (adjustDistance > 0 && Math.abs(dir) > adjustDistance) { ++ double minX = Double.NEGATIVE_INFINITY; ++ double minZ = Double.NEGATIVE_INFINITY; ++ double maxX = Double.POSITIVE_INFINITY; ++ double maxZ = Double.POSITIVE_INFINITY; ++ if (xAdjust) { // limit x adjust ++ minX = Math.floor(currBoundingBox.minX) - adjustDistance; ++ maxX = Math.floor(currBoundingBox.maxX) + adjustDistance + 1; ++ } else { // limit z adjust ++ minZ = Math.floor(currBoundingBox.minZ) - adjustDistance; ++ maxZ = Math.floor(currBoundingBox.maxZ) + adjustDistance + 1; ++ } ++ VoxelShape safeSpace = Shapes.box( ++ minX, Double.NEGATIVE_INFINITY, minZ, ++ maxX, Double.POSITIVE_INFINITY, maxZ ++ ); ++ shapes.add(Shapes.join(Shapes.INFINITY, safeSpace, BooleanOp.ONLY_FIRST)); ++ } ++ } ++ // Sakura end - configurable left shooting and adjusting limits + + public Entity(EntityType entityType, Level level) { + this.type = entityType; +@@ -1641,6 +1681,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + if (xSmaller && z != 0.0) { ++ this.limitAdjustMovement(currBoundingBox, z, false, voxelList); // Sakura - configurable left shooting and adjusting limits + z = this.scanZ(currBoundingBox, z, voxelList, bbList); + if (z != 0.0) { + currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetZ(currBoundingBox, z); +@@ -1648,6 +1689,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + if (x != 0.0) { ++ // Sakura start - configurable left shooting and adjusting limits ++ if (!xSmaller) { ++ this.limitAdjustMovement(currBoundingBox, x, true, voxelList); ++ } ++ // Sakura end - configurable left shooting and adjusting limits + x = this.scanX(currBoundingBox, x, voxelList, bbList); + if (x != 0.0) { + currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetX(currBoundingBox, x); +diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java +index d82e5184515c14a819b70c4d621cc6d93d120cc9..77656b3e8b80b07e9aa44a4d2c9bca468c52d26b 100644 +--- a/net/minecraft/world/entity/item/FallingBlockEntity.java ++++ b/net/minecraft/world/entity/item/FallingBlockEntity.java +@@ -263,6 +263,7 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti + // Sakura end - configure cannon physics + this.time++; + this.applyGravity(); ++ this.limitLeftShooting(); // Sakura - configurable left shooting and adjusting limits + this.moveStripped(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement + this.applyEffectsFromBlocks(); + // Paper start - Configurable falling blocks height nerf diff --git a/sakura-server/minecraft-patches/features/0023-Optimise-hopper-ticking.patch b/sakura-server/minecraft-patches/features/0023-Optimise-hopper-ticking.patch new file mode 100644 index 0000000..2ca023b --- /dev/null +++ b/sakura-server/minecraft-patches/features/0023-Optimise-hopper-ticking.patch @@ -0,0 +1,315 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Mon, 12 Aug 2024 15:35:57 +0100 +Subject: [PATCH] Optimise hopper ticking + + +diff --git a/net/minecraft/world/CompoundContainer.java b/net/minecraft/world/CompoundContainer.java +index 82923c9db0c620fe83c4d4447e6eb0dd8a6bb334..0dbc419884bd407dcbd44f7f0aef89754526a18f 100644 +--- a/net/minecraft/world/CompoundContainer.java ++++ b/net/minecraft/world/CompoundContainer.java +@@ -48,6 +48,15 @@ public class CompoundContainer implements Container { + return this.container1.getLocation(); // TODO: right? + } + // CraftBukkit end ++ // Sakura start - optimise hopper ticking ++ @Override ++ public final boolean addListener(net.minecraft.world.level.block.entity.BlockEntity.BlockEntityChangeListener listener) { ++ boolean result = false; ++ result |= this.container1.addListener(listener); ++ result |= this.container2.addListener(listener); ++ return result; ++ } ++ // Sakura end - optimise hopper ticking + + public CompoundContainer(Container container1, Container container2) { + this.container1 = container1; +diff --git a/net/minecraft/world/Container.java b/net/minecraft/world/Container.java +index 2d3721e311851c1801b090e99d4f9d0daf4e5f99..2249f5338f97471a833acddcee95f6a769176ce8 100644 +--- a/net/minecraft/world/Container.java ++++ b/net/minecraft/world/Container.java +@@ -12,6 +12,12 @@ import net.minecraft.world.level.block.entity.BlockEntity; + public interface Container extends Clearable { + float DEFAULT_DISTANCE_BUFFER = 4.0F; + ++ // Sakura start - optimise hopper ticking ++ default boolean addListener(BlockEntity.BlockEntityChangeListener container) { ++ return false; ++ } ++ // Sakura end - optimise hopper ticking ++ + int getContainerSize(); + + boolean isEmpty(); +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index eeb37d088cec5b2b8e1ac4bd48b4491eed0822e2..28fc6372b3e4815fa62217a27f265f182046fe73 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -1659,7 +1659,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + // Spigot end + if (tickingBlockEntity.isRemoved()) { + toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll +- } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { ++ } else if (runsNormally && tickingBlockEntity.isBlockEntityActive() && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { // Sakura - optimise hopper ticking + tickingBlockEntity.tick(); + // Paper start - rewrite chunk system + if ((++tickedEntities & 7) == 0) { +diff --git a/net/minecraft/world/level/block/HopperBlock.java b/net/minecraft/world/level/block/HopperBlock.java +index 8ce44400279511f78a0773a23a2aafc3f887ade3..1457d9feee282e375a56b19423cc6fb561521d13 100644 +--- a/net/minecraft/world/level/block/HopperBlock.java ++++ b/net/minecraft/world/level/block/HopperBlock.java +@@ -140,6 +140,12 @@ public class HopperBlock extends BaseEntityBlock { + private void checkPoweredState(Level level, BlockPos pos, BlockState state) { + boolean flag = !level.hasNeighborSignal(pos); + if (flag != state.getValue(ENABLED)) { ++ // Sakura start - optimise hopper ticking ++ final BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof HopperBlockEntity hbe && level.sakuraConfig().technical.optimiseIdleHopperTicking) { ++ hbe.setBlockEntityTicking(flag); ++ } ++ // Sakura end - optimise hopper ticking + level.setBlock(pos, state.setValue(ENABLED, Boolean.valueOf(flag)), 2); + } + } +diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java +index 77618757c0e678532dbab814aceed83f7f1cd892..65d359c497b73c821b082a885c063a7328d378a9 100644 +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -40,6 +40,55 @@ public abstract class BlockEntity { + private BlockState blockState; + private DataComponentMap components = DataComponentMap.EMPTY; + ++ // Sakura start - optimise hopper ticking ++ private final Set listeners = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0); ++ private final java.util.List listeningBlocks = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(0); ++ private boolean blockEntityTicking = true; ++ private int tickCount = 0; ++ ++ public final int getIdleTickCount() { ++ return this.tickCount; ++ } ++ ++ public final boolean isBlockEntityActive() { ++ this.tickCount++; ++ return this.blockEntityTicking; ++ } ++ ++ public final void setBlockEntityTicking(boolean blockEntityTicking) { ++ this.tickCount = 0; ++ this.blockEntityTicking = blockEntityTicking; ++ } ++ ++ public final boolean addListener(BlockEntityChangeListener listener) { ++ if (this.listeners.add(listener)) { ++ ((BlockEntity) listener).listeningBlocks.add(this); ++ } ++ return true; ++ } ++ ++ public final void updateListeners(boolean onRemove) { ++ for (BlockEntityChangeListener listener : this.listeners) { ++ if (onRemove) { ++ listener.neighborRemoved(); ++ } else { ++ listener.neighborChange(); ++ } ++ } ++ if (onRemove) { ++ this.listeningBlocks.forEach(blockEntity -> blockEntity.listeners.clear()); ++ this.listeningBlocks.clear(); ++ this.listeners.clear(); ++ } ++ } ++ ++ public interface BlockEntityChangeListener { ++ void neighborChange(); ++ ++ void neighborRemoved(); ++ } ++ // Sakura end - optimise hopper ticking ++ + public BlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + this.type = type; + this.worldPosition = pos.immutable(); +@@ -198,11 +247,22 @@ public abstract class BlockEntity { + public void setChanged() { + if (this.level != null) { + if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers +- setChanged(this.level, this.worldPosition, this.blockState); ++ setChanged(this.level, this.worldPosition, this.blockState, this); // Sakura - optimise hopper ticking + } + } + + protected static void setChanged(Level level, BlockPos pos, BlockState state) { ++ // Sakura start - optimise hopper ticking ++ net.minecraft.world.level.chunk.LevelChunk chunk = level.getChunkIfLoaded(pos); ++ BlockEntity blockEntity = chunk != null ? chunk.getBlockEntity(pos) : null; ++ setChanged(level, pos, state, blockEntity); ++ } ++ ++ protected static void setChanged(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity) { ++ if (blockEntity != null) { ++ blockEntity.updateListeners(false); ++ } ++ // Sakura end - optimise hopper ticking + level.blockEntityChanged(pos); + if (!state.isAir()) { + level.updateNeighbourForOutputSignal(pos, state.getBlock()); +@@ -232,6 +292,7 @@ public abstract class BlockEntity { + + public void setRemoved() { + this.remove = true; ++ this.updateListeners(true); // Sakura - optimise hopper ticking + } + + public void clearRemoved() { +diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +index 5cd1326ad5d046c88b2b3449d610a78fa880b4cd..900ed60d6294a8afea48800808a7b89a8a8b27ad 100644 +--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +@@ -28,7 +28,7 @@ import net.minecraft.world.level.block.HopperBlock; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.phys.AABB; + +-public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper { ++public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, BlockEntity.BlockEntityChangeListener { // Sakura - optimise hopper ticking + public static final int MOVE_ITEM_SPEED = 8; + public static final int HOPPER_CONTAINER_SIZE = 5; + private static final int[][] CACHED_SLOTS = new int[54][]; +@@ -66,6 +66,58 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + this.maxStack = size; + } + // CraftBukkit end ++ // Sakura start - optimise hopper ticking ++ private static final int SOURCE_CONTAINER = 1 << 0; ++ private static final int ATTACHED_CONTAINER = 1 << 1; ++ private int connectedContainers = 0; ++ ++ @Override ++ public final void neighborChange() { ++ this.startTicking(); ++ } ++ ++ @Override ++ public final void neighborRemoved() { ++ this.connectedContainers = 0; ++ this.startTicking(); ++ } ++ ++ private void startTicking() { ++ this.cooldownTime -= this.getIdleTickCount(); ++ this.setBlockEntityTicking(true); ++ } ++ ++ private void waitForChange(int fullState) { ++ if ((fullState == HOPPER_IS_FULL || (this.connectedContainers & SOURCE_CONTAINER) != 0) && (this.connectedContainers & ATTACHED_CONTAINER) != 0) { ++ this.addListener(this); ++ this.setBlockEntityTicking(false); ++ } ++ } ++ ++ private static @Nullable Container sakura_getSourceContainer(Level level, Hopper hopper, BlockPos pos, BlockState state) { ++ Container container = getSourceContainer(level, hopper, pos, state); ++ if (hopper instanceof HopperBlockEntity hbe && HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) { ++ hbe.listenForContainerChanges(container, SOURCE_CONTAINER); ++ } ++ return container; ++ } ++ ++ private static @Nullable Container sakura_getAttachedContainer(Level level, BlockPos pos, HopperBlockEntity hbe) { ++ Container container = getAttachedContainer(level, pos, hbe); ++ if (HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) { ++ hbe.listenForContainerChanges(container, ATTACHED_CONTAINER); ++ } ++ return container; ++ } ++ ++ private void listenForContainerChanges(@Nullable Container container, int type) { ++ if (container != null && container.addListener(this)) { ++ this.connectedContainers |= type; // set ++ } else if ((this.connectedContainers & type) != 0) { ++ this.connectedContainers ^= type; // unset ++ } ++ } ++ // Sakura end - optimise hopper ticking + + + public HopperBlockEntity(BlockPos pos, BlockState blockState) { +@@ -196,6 +248,12 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + setChanged(level, pos, state); + return true; + } ++ ++ // Sakura start - optimise hopper ticking ++ if (level.sakuraConfig().technical.optimiseIdleHopperTicking) { ++ blockEntity.waitForChange(fullState); ++ } ++ // Sakura end - optimise hopper ticking + } + + return false; +@@ -413,7 +471,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + // Paper end - Perf: Optimize Hoppers + + private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) { +- Container attachedContainer = getAttachedContainer(level, pos, blockEntity); ++ Container attachedContainer = sakura_getAttachedContainer(level, pos, blockEntity); // Sakura - optimise hopper ticking + if (attachedContainer == null) { + return false; + } else { +@@ -526,7 +584,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + public static boolean suckInItems(Level level, Hopper hopper) { + BlockPos blockPos = BlockPos.containing(hopper.getLevelX(), hopper.getLevelY() + 1.0, hopper.getLevelZ()); + BlockState blockState = level.getBlockState(blockPos); +- Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState); ++ Container sourceContainer = sakura_getSourceContainer(level, hopper, blockPos, blockState); // Sakura - optimise hopper ticking + if (sourceContainer != null) { + Direction direction = Direction.DOWN; + skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers +diff --git a/net/minecraft/world/level/block/entity/TickingBlockEntity.java b/net/minecraft/world/level/block/entity/TickingBlockEntity.java +index 28e3b73507b988f7234cbf29c4024c88180d0aef..a0d247aa883553708c4b92158232425593d50534 100644 +--- a/net/minecraft/world/level/block/entity/TickingBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/TickingBlockEntity.java +@@ -10,4 +10,10 @@ public interface TickingBlockEntity { + BlockPos getPos(); + + String getType(); ++ ++ // Sakura start - optimise hopper ticking ++ default boolean isBlockEntityActive() { ++ return true; ++ } ++ // Sakura end - optimise hopper ticking + } +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..04477dd45088c06c97c2f8e24e9d1a7362a62a61 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -955,6 +955,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + return BlockEntityType.getKey(this.blockEntity.getType()).toString(); + } + ++ // Sakura start - optimise hopper ticking ++ @Override ++ public boolean isBlockEntityActive() { ++ return this.blockEntity.isBlockEntityActive(); ++ } ++ // Sakura end - optimise hopper ticking ++ + @Override + public String toString() { + return "Level ticker for " + this.getType() + "@" + this.getPos(); +@@ -1003,6 +1010,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + return this.ticker.getType(); + } + ++ // Sakura start - optimise hopper ticking ++ @Override ++ public boolean isBlockEntityActive() { ++ return this.ticker.isBlockEntityActive(); ++ } ++ // Sakura end - optimise hopper ticking ++ + @Override + public String toString() { + return this.ticker + " "; diff --git a/sakura-server/minecraft-patches/features/0024-Optimise-entity-scheduler-ticking.patch b/sakura-server/minecraft-patches/features/0024-Optimise-entity-scheduler-ticking.patch new file mode 100644 index 0000000..6129f91 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0024-Optimise-entity-scheduler-ticking.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 13 Sep 2024 17:22:51 +0100 +Subject: [PATCH] Optimise entity scheduler ticking + + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java +index 26207443b1223119c03db478d7e816d9cdf8e618..1664830a49f37825c39fb6b436011d8149196e3a 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java +@@ -18,6 +18,22 @@ public final class ServerEntityLookup extends EntityLookup { + + private final ServerLevel serverWorld; + public final ReferenceList trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker ++ // Sakura start - optimise entity scheduler ticking ++ public final ReferenceList scheduledEntities = new ReferenceList<>(); ++ ++ public void entityStartScheduled(final Entity entity) { ++ org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity = entity.getBukkitEntityRaw(); ++ if (bukkitEntity != null && bukkitEntity.taskScheduler.hasTask()) { ++ this.scheduledEntities.add(bukkitEntity); ++ } ++ } ++ ++ public void entityEndScheduled(final Entity entity) { ++ if (entity.getBukkitEntityRaw() != null) { ++ this.scheduledEntities.remove(entity.getBukkitEntityRaw()); ++ } ++ } ++ // Sakura end - optimise entity scheduler ticking + + public ServerEntityLookup(final ServerLevel world, final LevelCallback worldCallback) { + super(world, worldCallback); +@@ -89,6 +105,7 @@ public final class ServerEntityLookup extends EntityLookup { + // Moonrise start - entity tracker + this.trackerEntities.add(entity); + // Moonrise end - entity tracker ++ this.entityStartScheduled(entity); // Sakura - optimise entity scheduler ticking + } + + @Override +@@ -96,6 +113,7 @@ public final class ServerEntityLookup extends EntityLookup { + // Moonrise start - entity tracker + this.trackerEntities.remove(entity); + // Moonrise end - entity tracker ++ this.entityEndScheduled(entity); // Sakura - optimise entity scheduler ticking + } + + @Override +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 6d8c513e78fa4efd8c7f6f534cf3958d46448efb..bad2fbd90d4db6b89b6346da53a41ef110307169 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1678,7 +1678,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { +- for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) { ++ // Sakura start - optimise entity scheduler ticking ++ final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) level.moonrise$getEntityLookup(); ++ final java.util.Iterator entityIterator = entityLookup.scheduledEntities.iterator(); ++ while (entityIterator.hasNext()) { ++ final org.bukkit.craftbukkit.entity.CraftEntity scheduledEntity = entityIterator.next(); ++ final net.minecraft.world.entity.Entity entity = scheduledEntity.getHandle(); ++ if (!scheduledEntity.taskScheduler.hasTask()) { ++ entityIterator.remove(); ++ continue; ++ } ++ // Sakura end - optimise entity scheduler ticking + if (entity.isRemoved()) { + continue; + } diff --git a/sakura-server/minecraft-patches/features/0025-Optimise-check-inside-blocks-and-traverse-blocks.patch b/sakura-server/minecraft-patches/features/0025-Optimise-check-inside-blocks-and-traverse-blocks.patch new file mode 100644 index 0000000..639a2d5 --- /dev/null +++ b/sakura-server/minecraft-patches/features/0025-Optimise-check-inside-blocks-and-traverse-blocks.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 8 Nov 2024 19:35:49 +0000 +Subject: [PATCH] Optimise check inside blocks and traverse blocks + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index f9b9d81cc60785488a7cf57caabb4a5fdd84ecae..17e5148f7fb273e6de3ab5f159cbfc5d4819eb9f 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -1930,6 +1930,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private void checkInsideBlocks(List movements, Set blocksInside) { + if (this.isAffectedByBlocks()) { + LongSet set = this.visitedBlocks; ++ // Sakura start - optimise check inside blocks ++ int lastChunkX = Integer.MIN_VALUE; ++ int lastChunkZ = Integer.MIN_VALUE; ++ net.minecraft.world.level.chunk.ChunkAccess chunk = null; ++ // Sakura end - optimise check inside blocks + + for (Entity.Movement movement : movements) { + Vec3 vec3 = movement.from(); +@@ -1949,7 +1954,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return; + } + +- BlockState blockState = this.level().getBlockState(blockPos); ++ // Sakura start - optimise check inside blocks ++ final int chunkX = blockPos.getX() >> 4; ++ final int chunkZ = blockPos.getZ() >> 4; ++ if (chunk == null || chunkX != lastChunkX || chunkZ != lastChunkZ) { ++ chunk = this.level.getChunkIfLoadedImmediately(chunkX, chunkZ); ++ if (chunk == null) { ++ continue; ++ } ++ lastChunkX = chunkX; ++ lastChunkZ = chunkZ; ++ } ++ final BlockState blockState = chunk.getBlockState(blockPos); ++ // Sakura end - optimise check inside blocks + if (!blockState.isAir() && set.add(blockPos.asLong())) { + try { + VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos); +diff --git a/net/minecraft/world/level/BlockGetter.java b/net/minecraft/world/level/BlockGetter.java +index 91865d7e78e15cc643a65de03045b90a52d6ec2a..8857887ac4fc9cb2bd08df4ccd9981d1d0e14806 100644 +--- a/net/minecraft/world/level/BlockGetter.java ++++ b/net/minecraft/world/level/BlockGetter.java +@@ -214,10 +214,18 @@ public interface BlockGetter extends LevelHeightAccessor { + + static Iterable boxTraverseBlocks(Vec3 oldPosition, Vec3 position, AABB boundingBox) { + Vec3 vec3 = position.subtract(oldPosition); +- Iterable iterable = BlockPos.betweenClosed(boundingBox); ++ // Sakura start - optimise check inside blocks + if (vec3.lengthSqr() < Mth.square(0.99999F)) { +- return iterable; ++ return me.samsuik.sakura.utils.BlockPosIterator.iterable(boundingBox); + } else { ++ final boolean xZero = vec3.x() == 0.0; ++ final boolean yZero = vec3.y() == 0.0; ++ final boolean zZero = vec3.z() == 0.0; ++ if (xZero && yZero || yZero && zZero || xZero && zZero) { ++ return me.samsuik.sakura.utils.BlockPosIterator.traverseArea(vec3, boundingBox); ++ } ++ Iterable iterable = BlockPos.betweenClosed(boundingBox); ++ // Sakura end - optimise check inside blocks + Set set = new ObjectLinkedOpenHashSet<>(); + Vec3 minPosition = boundingBox.getMinPosition(); + Vec3 vec31 = minPosition.subtract(vec3); diff --git a/sakura-server/paper-patches/features/0004-Optimise-entity-scheduler-ticking.patch b/sakura-server/paper-patches/features/0004-Optimise-entity-scheduler-ticking.patch new file mode 100644 index 0000000..a1e7f57 --- /dev/null +++ b/sakura-server/paper-patches/features/0004-Optimise-entity-scheduler-ticking.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 13 Sep 2024 17:22:51 +0100 +Subject: [PATCH] Optimise entity scheduler ticking + + +diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java +index c03608fec96b51e1867f43d8f42e5aefb1520e46..9a363b8a15d76911813e31fdb525815b74539b02 100644 +--- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java ++++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java +@@ -50,6 +50,22 @@ public final class EntityScheduler { + this.entity = Validate.notNull(entity); + } + ++ // Sakura start - optimise entity scheduler ticking ++ public boolean hasTask() { ++ return !this.currentlyExecuting.isEmpty() || !this.oneTimeDelayed.isEmpty(); ++ } ++ ++ private void newScheduledTask() { ++ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> { ++ Entity handle = this.entity.getHandleRaw(); ++ net.minecraft.server.level.ServerLevel level = (net.minecraft.server.level.ServerLevel) handle.level(); ++ ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) level.moonrise$getEntityLookup(); ++ ++ entityLookup.entityStartScheduled(handle); ++ }); ++ } ++ // Sakura end - optimise entity scheduler ticking ++ + /** + * Retires the scheduler, preventing new tasks from being scheduled and invoking the retired callback + * on all currently scheduled tasks. +@@ -128,6 +144,7 @@ public final class EntityScheduler { + return new ArrayList<>(); + }).add(task); + } ++ this.newScheduledTask(); // Sakura - tick entity schedulers only when necessary + + return true; + } diff --git a/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java index ce04a5d..c916d30 100644 --- a/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +++ b/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java @@ -161,9 +161,15 @@ public abstract class SpecialisedExplosion extends ServerExplo if (distanceFromBottom <= 1.0) { double x = entity.getX() - pos.x; - double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - pos.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/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java index ebe5f0c..320cdb1 100644 --- a/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +++ b/sakura-server/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java @@ -37,6 +37,13 @@ public final class TntExplosion extends SpecialisedExplosion { this.bounds = new AABB(center, center); } + // 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 int getExplosionCount() { if (this.cause.getMergeEntityData().getMergeLevel() == MergeLevel.NONE) { diff --git a/sakura-server/src/main/java/me/samsuik/sakura/utils/BlockPosIterator.java b/sakura-server/src/main/java/me/samsuik/sakura/utils/BlockPosIterator.java index e00c07c..7d97940 100644 --- a/sakura-server/src/main/java/me/samsuik/sakura/utils/BlockPosIterator.java +++ b/sakura-server/src/main/java/me/samsuik/sakura/utils/BlockPosIterator.java @@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.util.Mth; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -22,6 +23,14 @@ public final class BlockPosIterator extends AbstractIterator { return () -> new BlockPosIterator(bb); } + public static Iterable traverseArea(Vec3 vec, AABB boundingBox) { + double toTravel = Math.min(16.0 / vec.length(), 1.0); + Vec3 movement = vec.scale(toTravel); + AABB fromBB = boundingBox.move(-vec.x, -vec.y, -vec.z); + AABB searchArea = fromBB.expandTowards(movement); + return me.samsuik.sakura.utils.BlockPosIterator.iterable(searchArea); + } + public BlockPosIterator(AABB bb) { this.startX = Mth.floor(bb.minX); this.startY = Mth.floor(bb.minY); diff --git a/sakura-server/src/minecraft/java b/sakura-server/src/minecraft/java index 6e26d2a..368fba8 160000 --- a/sakura-server/src/minecraft/java +++ b/sakura-server/src/minecraft/java @@ -1 +1 @@ -Subproject commit 6e26d2a9a1f70b4a6ae41f86ebd4f319a01989d0 +Subproject commit 368fba8e7c278e533a5fe2e273e06b529fc7de80 diff --git a/sakura-server/src/minecraft/resources b/sakura-server/src/minecraft/resources index 6f9aaa6..9d71ffc 160000 --- a/sakura-server/src/minecraft/resources +++ b/sakura-server/src/minecraft/resources @@ -1 +1 @@ -Subproject commit 6f9aaa646da2dafea1b862ddb45bf0ac5b6e8a63 +Subproject commit 9d71ffc850d74e58a376f443edffd4b9ee37f6f6