From 5410f06e7e08e149cbc9335259a7e9288584fc53 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Thu, 13 Feb 2025 00:51:20 +0000 Subject: [PATCH] Move legacy explosion clipping out of Level --- .../0016-Configure-cannon-physics.patch | 179 +----------------- .../0024-Optimise-hopper-ticking.patch | 4 +- .../explosion/LegacyExplosionClipping.java | 179 ++++++++++++++++++ 3 files changed, 183 insertions(+), 179 deletions(-) create mode 100644 sakura-server/src/main/java/me/samsuik/sakura/explosion/LegacyExplosionClipping.java diff --git a/sakura-server/minecraft-patches/features/0016-Configure-cannon-physics.patch b/sakura-server/minecraft-patches/features/0016-Configure-cannon-physics.patch index 9a27d06..ba877dd 100644 --- a/sakura-server/minecraft-patches/features/0016-Configure-cannon-physics.patch +++ b/sakura-server/minecraft-patches/features/0016-Configure-cannon-physics.patch @@ -420,183 +420,8 @@ index d23193d3f11505cea428414487f891ab584ad071..ffd96218e2ee84aae7a008ef1b95f84d } // 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 7d98febf87a51fb7d9b502fa362d1bc4ac1e3eaa..911a665e1ae07b2665222e04a9e1b0a80af0f877 100644 ---- a/net/minecraft/world/level/Level.java -+++ b/net/minecraft/world/level/Level.java -@@ -844,6 +844,170 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - 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 -+ // 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 - - protected Level( - WritableLevelData levelData, diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index 7b5a50ef269d0ca59fb067258421b53971a9998d..a4ceca6cfaaf048f3f78b18724d54780675354e7 100644 +index 7b5a50ef269d0ca59fb067258421b53971a9998d..8aa4debbc68530670ba6329554da5e9cf8e64a71 100644 --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java @@ -412,6 +412,7 @@ public class ServerExplosion implements Explosion { @@ -621,7 +446,7 @@ index 7b5a50ef269d0ca59fb067258421b53971a9998d..a4ceca6cfaaf048f3f78b18724d54780 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); ++ hitResult = me.samsuik.sakura.explosion.LegacyExplosionClipping.clipLegacy(entity.level(), 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; diff --git a/sakura-server/minecraft-patches/features/0024-Optimise-hopper-ticking.patch b/sakura-server/minecraft-patches/features/0024-Optimise-hopper-ticking.patch index 5cd558b..ce434dc 100644 --- a/sakura-server/minecraft-patches/features/0024-Optimise-hopper-ticking.patch +++ b/sakura-server/minecraft-patches/features/0024-Optimise-hopper-ticking.patch @@ -42,10 +42,10 @@ index 2d3721e311851c1801b090e99d4f9d0daf4e5f99..2249f5338f97471a833acddcee95f6a7 boolean isEmpty(); diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index 911a665e1ae07b2665222e04a9e1b0a80af0f877..818887bef8265731b01f16f4edbfd88bc89cc4c9 100644 +index 7d98febf87a51fb7d9b502fa362d1bc4ac1e3eaa..cfbec037f4314d7425e4abf14c77e57752eb2ae3 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -1663,7 +1663,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1499,7 +1499,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl // Spigot end if (tickingBlockEntity.isRemoved()) { toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll diff --git a/sakura-server/src/main/java/me/samsuik/sakura/explosion/LegacyExplosionClipping.java b/sakura-server/src/main/java/me/samsuik/sakura/explosion/LegacyExplosionClipping.java new file mode 100644 index 0000000..9b3ab13 --- /dev/null +++ b/sakura-server/src/main/java/me/samsuik/sakura/explosion/LegacyExplosionClipping.java @@ -0,0 +1,179 @@ +package me.samsuik.sakura.explosion; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class LegacyExplosionClipping { + public static BlockHitResult.Type clipLegacy(Level level, 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 mutableBlockPos = new BlockPos.MutableBlockPos(fromX, fromY, fromZ); + LevelChunk chunk = level.getChunkIfLoaded(fromX >> 4, fromZ >> 4); + if (chunk == null) { + return BlockHitResult.Type.MISS; + } + + BlockState state = chunk.getBlockState(mutableBlockPos); + VoxelShape shape = state.getShape(level, mutableBlockPos); + for (AABB bb : shape.toAabbs()) { + if (clip(bb, mutableBlockPos, from, to)) { + return BlockHitResult.Type.BLOCK; + } + } + + for (int steps = 0; steps < 16; ++steps) { + if (fromX == toX && fromY == toY && fromZ == toZ) { + return 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); + mutableBlockPos.set(fromX, fromY, fromZ); + + int chunkX = fromX >> 4; + int chunkZ = fromZ >> 4; + if (chunkX != chunk.locX || chunkZ != chunk.locZ) { + chunk = level.getChunkIfLoaded(chunkX, chunkZ); + } + if (chunk == null) { + return BlockHitResult.Type.MISS; + } + + state = chunk.getBlockState(mutableBlockPos); + shape = state.getShape(level, mutableBlockPos); + for (AABB bb : shape.toAabbs()) { + if (clip(bb, mutableBlockPos, from, to)) { + return BlockHitResult.Type.BLOCK; + } + } + } + return BlockHitResult.Type.MISS; + } + + private static boolean clip(AABB bb, BlockPos pos, Vec3 from, Vec3 to) { + from = from.subtract(pos.getX(), pos.getY(), pos.getZ()); + to = to.subtract(pos.getX(), pos.getY(), pos.getZ()); + + double x = to.x - from.x; + double y = to.y - from.y; + double z = to.z - from.z; + + double minXd = clip(bb.minX, x, from.x); + double minYd = clip(bb.minY, y, from.y); + double minZd = clip(bb.minZ, z, from.z); + double maxXd = clip(bb.maxX, x, from.x); + double maxYd = clip(bb.maxY, y, from.y); + double maxZd = clip(bb.maxZ, z, from.z); + + return clipX(from, bb, minXd, y, z) || clipY(from, bb, minYd, x, z) || clipZ(from, bb, minZd, x, y) + || clipX(from, bb, maxXd, y, z) || clipY(from, bb, maxYd, x, z) || clipZ(from, bb, maxZd, x, y); + } + + private static double clip(double bound, double axisD, double axisN) { + if (axisD * axisD < 1.0000000116860974E-7D) { + return -1.0; + } + return (bound - axisN) / axisD; + } + + private static boolean clipX(Vec3 from, AABB bb, double n, double y, double z) { + if (n < 0.0 || n > 1.0) { + return false; + } + y = from.y + y * n; + z = from.z + z * n; + return y >= bb.minY && y <= bb.maxY && z >= bb.minZ && z <= bb.maxZ; + } + + private static boolean clipY(Vec3 from, AABB bb, double n, double x, double z) { + if (n < 0.0 || n > 1.0) { + return false; + } + x = from.x + x * n; + z = from.z + z * n; + return x >= bb.minX && x <= bb.maxX && z >= bb.minZ && z <= bb.maxZ; + } + + private static boolean clipZ(Vec3 from, AABB bb, double n, double x, double y) { + if (n < 0.0 || n > 1.0) { + return false; + } + x = from.x + x * n; + y = from.y + y * n; + return x >= bb.minX && x <= bb.maxX && y >= bb.minY && y <= bb.maxY; + } +}