diff --git a/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch b/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch new file mode 100644 index 00000000..0ac1f020 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0160-Optimize-applyEffectsFromBlocks-and-checkInsideBlock.patch @@ -0,0 +1,246 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Wed, 9 Apr 2025 18:46:23 +0200 +Subject: [PATCH] Optimize applyEffectsFromBlocks and checkInsideBlocks + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 075fcbcde23b5bb7b27ff622e8d188c3a2583973..3a791927fa33be4346f28b55325a0340445731ae 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -9,6 +9,8 @@ import com.mojang.logging.LogUtils; + import it.unimi.dsi.fastutil.floats.FloatArraySet; + import it.unimi.dsi.fastutil.floats.FloatArrays; + import it.unimi.dsi.fastutil.floats.FloatSet; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongSet; + import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap; +@@ -832,7 +834,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + protected AABB makeBoundingBox(Vec3 position) { +- return this.dimensions.makeBoundingBox(position); ++ if (this.lastDimensions == null || this.lastDimensions.width() != this.dimensions.width() || this.lastDimensions.height() != this.dimensions.height()) { ++ this.lastDimensions = this.dimensions; ++ this.cachedBoundingBox = this.dimensions.makeBoundingBox(Vec3.ZERO); ++ } ++ ++ return this.cachedBoundingBox.move(position); + } + + protected void reapplyPosition() { +@@ -1351,33 +1358,56 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void applyEffectsFromBlocks(Vec3 oldPosition, Vec3 position) { +- if (this.isAffectedByBlocks()) { +- if (this.onGround()) { +- BlockPos onPosLegacy = this.getOnPosLegacy(); ++ if (!this.isAffectedByBlocks()) { ++ return; ++ } ++ ++ if (this.onGround()) { ++ BlockPos onPosLegacy = this.getOnPosLegacy(); ++ if (this.cachedSupportingBlockState == null || !this.isSupportedBy(onPosLegacy)) { + BlockState blockState = this.level().getBlockState(onPosLegacy); + blockState.getBlock().stepOn(this.level(), onPosLegacy, blockState, this); ++ this.cachedSupportingBlockState = blockState; ++ } else { ++ this.cachedSupportingBlockState.getBlock().stepOn(this.level(), onPosLegacy, this.cachedSupportingBlockState, this); + } ++ } + ++ if (oldPosition.distanceToSqr(position) > 1.0E-8) { + this.movementThisTick.add(new Entity.Movement(oldPosition, position)); +- List list = List.copyOf(this.movementThisTick); +- this.movementThisTick.clear(); +- this.checkInsideBlocks(list, this.blocksInside); +- boolean flag = Iterables.any(this.blocksInside, state -> state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)); +- this.blocksInside.clear(); +- if (!flag && this.isAlive()) { +- if (this.remainingFireTicks <= 0) { +- this.setRemainingFireTicks(-this.getFireImmuneTicks()); +- } ++ } + +- if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { +- this.playEntityOnFireExtinguishedSound(); +- } ++ if (this.movementThisTick.isEmpty()) { ++ return; ++ } ++ ++ this.checkInsideBlocks(this.movementThisTick, this.blocksInside); ++ ++ boolean inFireOrLava = false; ++ for (BlockState state : this.blocksInside) { ++ if (state.is(BlockTags.FIRE) || state.is(Blocks.LAVA)) { ++ inFireOrLava = true; ++ break; + } ++ } ++ ++ this.movementThisTick.clear(); + +- if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ if (!inFireOrLava && this.isAlive()) { ++ if (this.remainingFireTicks <= 0) { + this.setRemainingFireTicks(-this.getFireImmuneTicks()); + } ++ ++ if (this.wasOnFire && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ this.playEntityOnFireExtinguishedSound(); ++ } + } ++ ++ if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble())) { ++ this.setRemainingFireTicks(-this.getFireImmuneTicks()); ++ } ++ ++ this.blocksInside.clear(); + } + + public boolean isAffectedByBlocks() { +@@ -1706,50 +1736,109 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public void recordMovementThroughBlocks(Vec3 oldPosition, Vec3 position) { + this.movementThisTick.add(new Entity.Movement(oldPosition, position)); + } ++ private final Int2ObjectMap blockStateCache = new Int2ObjectOpenHashMap<>(16); ++ private BlockPos lastBlockPos = BlockPos.ZERO; ++ private BlockState lastBlockState = null; + + private void checkInsideBlocks(List movements, Set blocksInside) { +- if (this.isAffectedByBlocks()) { +- LongSet set = this.visitedBlocks; ++ if (!this.isAffectedByBlocks()) { ++ return; ++ } ++ ++ this.visitedBlocks.clear(); ++ this.blockStateCache.clear(); ++ ++ for (Entity.Movement movement : movements) { ++ Vec3 fromPos = movement.from(); ++ Vec3 toPos = movement.to(); + +- for (Entity.Movement movement : movements) { +- Vec3 vec3 = movement.from(); +- Vec3 vec31 = movement.to(); +- AABB aabb = this.makeBoundingBox(vec31).deflate(1.0E-5F); ++ if (fromPos.distanceToSqr(toPos) < 1.0E-8) { ++ continue; ++ } ++ ++ AABB aabb = this.makeBoundingBox(toPos).deflate(1.0E-5F); ++ ++ int minX = Mth.floor(Math.min(fromPos.x, toPos.x) - aabb.getXsize()/2); ++ int maxX = Mth.ceil(Math.max(fromPos.x, toPos.x) + aabb.getXsize()/2); ++ int minY = Mth.floor(Math.min(fromPos.y, toPos.y) - aabb.getYsize()/2); ++ int maxY = Mth.ceil(Math.max(fromPos.y, toPos.y) + aabb.getYsize()/2); ++ int minZ = Mth.floor(Math.min(fromPos.z, toPos.z) - aabb.getZsize()/2); ++ int maxZ = Mth.ceil(Math.max(fromPos.z, toPos.z) + aabb.getZsize()/2); ++ ++ int minChunkX = minX >> 4; ++ int maxChunkX = maxX >> 4; ++ int minChunkZ = minZ >> 4; ++ int maxChunkZ = maxZ >> 4; + +- for (BlockPos blockPos : BlockGetter.boxTraverseBlocks(vec3, vec31, aabb)) { +- if (!this.isAlive()) { +- return; ++ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); ++ ++ for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { ++ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { ++ if (!level().hasChunkAt(chunkX, chunkZ)) { ++ continue; + } + +- BlockState blockState = this.level().getBlockState(blockPos); +- if (!blockState.isAir() && set.add(blockPos.asLong())) { +- try { +- VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos); +- if (entityInsideCollisionShape != Shapes.block() +- && !this.collidedWithShapeMovingFrom(vec3, vec31, blockPos, entityInsideCollisionShape)) { +- continue; +- } ++ int xStart = Math.max(minX, chunkX << 4); ++ int xEnd = Math.min(maxX, (chunkX << 4) + 15); ++ int zStart = Math.max(minZ, chunkZ << 4); ++ int zEnd = Math.min(maxZ, (chunkZ << 4) + 15); + +- blockState.entityInside(this.level(), blockPos, this); +- this.onInsideBlock(blockState); +- } catch (Throwable var16) { +- CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); +- CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); +- CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), blockPos, blockState); +- CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); +- this.fillCrashReportCategory(crashReportCategory1); +- throw new ReportedException(crashReport); +- } ++ for (int x = xStart; x <= xEnd; x++) { ++ for (int z = zStart; z <= zEnd; z++) { ++ for (int y = minY; y <= maxY; y++) { ++ if (!this.isAlive()) { ++ return; ++ } ++ ++ mutablePos.set(x, y, z); ++ long posLong = mutablePos.asLong(); ++ ++ if (!this.visitedBlocks.add(posLong)) { ++ continue; ++ } + +- blocksInside.add(blockState); ++ BlockState blockState; ++ if (mutablePos.equals(this.lastBlockPos)) { ++ blockState = this.lastBlockState; ++ } else { ++ blockState = this.level().getBlockState(mutablePos); ++ this.lastBlockPos = mutablePos.immutable(); ++ this.lastBlockState = blockState; ++ } ++ ++ if (blockState.isAir()) { ++ continue; ++ } ++ ++ try { ++ VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), mutablePos); ++ if (entityInsideCollisionShape != Shapes.block() && ++ !this.collidedWithShapeMovingFrom(fromPos, toPos, mutablePos, entityInsideCollisionShape)) { ++ continue; ++ } ++ ++ blockState.entityInside(this.level(), mutablePos, this); ++ this.onInsideBlock(blockState); ++ blocksInside.add(blockState); ++ } catch (Throwable var16) { ++ CrashReport crashReport = CrashReport.forThrowable(var16, "Colliding entity with block"); ++ CrashReportCategory crashReportCategory = crashReport.addCategory("Block being collided with"); ++ CrashReportCategory.populateBlockDetails(crashReportCategory, this.level(), mutablePos, blockState); ++ CrashReportCategory crashReportCategory1 = crashReport.addCategory("Entity being checked for collision"); ++ this.fillCrashReportCategory(crashReportCategory1); ++ throw new ReportedException(crashReport); ++ } ++ } ++ } + } + } + } +- +- set.clear(); + } + } + ++ private EntityDimensions lastDimensions = null; ++ private AABB cachedBoundingBox = null; ++ + private boolean collidedWithShapeMovingFrom(Vec3 oldPosition, Vec3 position, BlockPos pos, VoxelShape shape) { + AABB aabb = this.makeBoundingBox(oldPosition); + Vec3 vec3 = position.subtract(oldPosition);