Files
Lophine/lophine-server/minecraft-patches/features/0135-Anonymous-Object-Lithium-optimized-non-poi-block-sea.patch
T
2026-07-28 15:37:41 +08:00

283 lines
18 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 28 Jul 2026 15:09:19 +0800
Subject: [PATCH] Anonymous Object: Lithium optimized non poi block searching
As requested by the original author, this patch will be anonymous and hide the original commit address.
This patch is used with permission from the original author.
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
index c6e9b155c2341c13d569f28f54f0c1d2d7313676..927635518c51375315390877d582b32fc38748df 100644
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
@@ -5,7 +5,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.level.LevelReader;
-public abstract class MoveToBlockGoal extends Goal {
+public abstract class MoveToBlockGoal extends Goal implements net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.LithiumMoveToBlockGoal { // Anonymous - Lithium Optimizes move to block goal
private static final int GIVE_UP_TICKS = 1200;
private static final int STAY_TICKS = 1200;
private static final int INTERVAL_TICKS = 200;
@@ -133,4 +133,167 @@ public abstract class MoveToBlockGoal extends Goal {
}
protected abstract boolean isValidTarget(LevelReader level, BlockPos pos);
+ // Anonymous start - Lithium Optimizes move to block goal
+ /**
+ * Finds the nearest block matching the predicates.
+ * <p>
+ * Side effect: The matching block position is stored in the blockPos field.
+ *
+ * @return Whether a matching block was found.
+ */
+ @Override
+ public boolean lithium$findNearestBlock(java.util.function.Predicate<net.minecraft.world.level.block.state.BlockState> requiredBlock, java.util.function.BiPredicate<net.minecraft.world.level.chunk.ChunkAccess,
+ BlockPos.MutableBlockPos> lithium$isValidTarget, final boolean shouldChunkLoad) {
+ //Center of the search starts 1 block below the mob's block position
+ BlockPos center = this.mob.blockPosition().offset(0,-1,0);
+
+ //Range is +-(searchRange - 1), +-verticalSearchRange, +-(searchRange - 1)
+ //Cache ChunkAccesses - getting them is surprisingly expensive - and track whether subchunks have the block
+ final LevelReader levelReader = this.mob.level();
+ net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CheckAndCacheBlockChecker checker = new net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CheckAndCacheBlockChecker(center,
+ this.searchRange-1, this.verticalSearchRange,
+ levelReader, requiredBlock, shouldChunkLoad);
+ it.unimi.dsi.fastutil.longs.LongArrayList sortedChunksMaybeWithBlock = new it.unimi.dsi.fastutil.longs.LongArrayList(checker.getChunkSize());
+ checker.initializeChunks(sortedChunksMaybeWithBlock::addLast);
+
+ if (checker.shouldStop()) {
+ return false; //No chunks with the target block - return early
+ }
+
+ final int minY = net.caffeinemc.mods.lithium.common.util.Pos.BlockCoord.getMinY(levelReader);
+ final int maxY = net.caffeinemc.mods.lithium.common.util.Pos.BlockCoord.getMaxYInclusive(levelReader);
+
+ // Prefer chunk aware search because it also cuts iterations inside "empty" chunk sections
+ if(!checker.hasUnloadedPossibleChunks()){
+ return this.lithium$chunkAwareSearch(center, lithium$isValidTarget, checker, sortedChunksMaybeWithBlock, minY, maxY);
+ }
+
+ // Use vanilla search because unordered search may observably alter chunk-loading behavior
+ return this.lithium$vanillaOrderSearch(center, lithium$isValidTarget, checker, minY, maxY);
+ }
+
+ private boolean lithium$vanillaOrderSearch(BlockPos center,
+ java.util.function.BiPredicate<net.minecraft.world.level.chunk.ChunkAccess, BlockPos.MutableBlockPos> lithium$isValidTarget,
+ net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CheckAndCacheBlockChecker checker, final int minY, final int maxY) {
+ BlockPos.MutableBlockPos currentPos = new BlockPos.MutableBlockPos();
+ final int centerY = center.getY();
+
+ for (int layer = this.verticalSearchStart; layer <= this.verticalSearchRange; layer = layer > 0 ? -layer : 1 - layer) {
+ final int y = centerY + layer;
+
+ // Layer outside of build limit - skip
+ // Note: this is likely to be hit because farms where this lags tend to be built at world floor
+ if (y < minY || y > maxY) {
+ continue;
+ }
+
+ for (int ring = 0; ring < this.searchRange; ring++) {
+ for (int dX = 0; dX <= ring; dX = dX > 0 ? -dX : 1 - dX) {
+ for (int dZ = dX < ring && dX > -ring ? ring : 0; dZ <= ring; dZ = dZ > 0 ? -dZ : 1 - dZ) {
+ currentPos.setWithOffset(center, dX, layer, dZ);
+ if (this.mob.isWithinHome(currentPos) && checker.checkPosition(currentPos)) {
+ // ChunkAccess is always loaded at this point
+ net.minecraft.world.level.chunk.ChunkAccess chunkAccess = checker.getCachedChunkAccess(currentPos);
+ if (lithium$isValidTarget.test(chunkAccess, currentPos)){
+ this.blockPos = currentPos;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean lithium$chunkAwareSearch(BlockPos center,
+ java.util.function.BiPredicate<net.minecraft.world.level.chunk.ChunkAccess, BlockPos.MutableBlockPos> lithium$isValidTarget,
+ net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CheckAndCacheBlockChecker checker, it.unimi.dsi.fastutil.longs.LongArrayList sortedChunksMaybeWithBlock,
+ final int minY, final int maxY) {
+ // Sort chunks by lowest sort order - has the earliest searched position
+ // Note: In this search order, the closest point normally is also the closest point in the search
+ sortedChunksMaybeWithBlock.sort((chunkLong0, chunkLong1) ->
+ net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.NonPOISearchDistances.MoveToBlockGoalDistances.getMinimumSortOrderOfChunk(center, chunkLong0)
+ - net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.NonPOISearchDistances.MoveToBlockGoalDistances.getMinimumSortOrderOfChunk(center, chunkLong1)
+ );
+
+ java.util.function.Predicate<net.minecraft.world.level.block.state.BlockState> requiredBlock = checker.blockStatePredicate;
+ final int minSectionY = checker.minSectionY;
+
+ BlockPos.MutableBlockPos foundPos = new BlockPos.MutableBlockPos();
+ BlockPos.MutableBlockPos currentPos = new BlockPos.MutableBlockPos();
+
+ // Same layer order as vanilla - saves iterations if targets are found in the first layer
+ for (int layer = this.verticalSearchStart; layer <= this.verticalSearchRange; layer = layer > 0 ? -layer : 1 - layer) {
+ final int y = center.getY() + layer;
+
+ // Layer outside of build limit - skip
+ // Note: this is likely to be hit because farms where this lags tend to be built at world floor
+ if (y < minY || y > maxY) {
+ continue;
+ }
+
+ final int chunkY = net.minecraft.core.SectionPos.blockToSectionCoord(y);
+ final int ySectionIndex = chunkY - minSectionY;
+
+ int closestFound = Integer.MAX_VALUE;
+ int ringMax = this.searchRange - 1;
+
+ // Iterate through slices of chunks that may have the target blockState
+ for (long chunkPos: sortedChunksMaybeWithBlock) {
+ final int chunkX = net.minecraft.world.level.ChunkPos.getX(chunkPos);
+ final int chunkZ = net.minecraft.world.level.ChunkPos.getZ(chunkPos);
+
+ // Break since no subsequent chunks can be closer
+ if (closestFound < net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.NonPOISearchDistances.MoveToBlockGoalDistances.getMinimumSortOrderOfChunk(center, chunkX, chunkZ)) {
+ break;
+ }
+
+ // Skip if the current subchunk does not have the block
+ if (!checker.checkCachedSection(chunkX, chunkY, chunkZ)) {
+ continue;
+ }
+
+ net.minecraft.world.level.chunk.ChunkAccess chunkAccess = checker.getCachedChunkAccess(chunkPos);
+ // If ChunkSection may have close enough targets, iterate layer in Paletted Container (x then z) order
+ final int chunkBlockX = net.minecraft.core.SectionPos.sectionToBlockCoord(chunkX);
+ int xMin = Math.max(center.getX() - ringMax, chunkBlockX);
+ int xMax = Math.min(center.getX() + ringMax, chunkBlockX + 15);
+ final int chunkBlockZ = net.minecraft.core.SectionPos.sectionToBlockCoord(chunkZ);
+ int zMin = Math.max(center.getZ() - ringMax, chunkBlockZ);
+ int zMax = Math.min(center.getZ() + ringMax, chunkBlockZ + 15);
+ net.minecraft.world.level.chunk.LevelChunkSection levelChunkSection = chunkAccess.getSections()[ySectionIndex];
+ for (int z = zMin; z <= zMax; z++) {
+ for (int x = xMin; x <= xMax; x++) {
+ int dX = x - center.getX();
+ int dZ = z - center.getZ();
+ int ring = net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.NonPOISearchDistances.MoveToBlockGoalDistances.getRing(dX, dZ);
+ int currentDistance = net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.NonPOISearchDistances.MoveToBlockGoalDistances.getVanillaSortOrderInt(ring, dX, dZ);
+ if (currentDistance < closestFound
+ && this.mob.isWithinHome(currentPos.set(x, y, z))
+ && requiredBlock.test(levelChunkSection.getBlockState(x & 15, y & 15, z & 15))
+ && lithium$isValidTarget.test(chunkAccess, currentPos)) {
+ // Constrain search size when we find a valid target
+ ringMax = ring;
+ xMin = Math.max(center.getX() - ringMax, chunkBlockX);
+ xMax = Math.min(center.getX() + ringMax, chunkBlockX + 15);
+ zMax = Math.min(center.getZ() + ringMax, chunkBlockZ + 15);
+ foundPos.set(x, y, z);
+ closestFound = currentDistance;
+ }
+ }
+ }
+ }
+
+ if (closestFound < Integer.MAX_VALUE) {
+ // Vanilla uses the mutable pos, no need to create immutable copy
+ this.blockPos = foundPos;
+ return true;
+ }
+ }
+
+ return false;
+ }
+ // Anonymous end - Lithium Optimizes move to block goal
}
diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
index ff0f99d4b560a1c0721885b0eba5bd5ab2087bd7..99c076ee2fe0d8294398c5ef1f24a95da5eee89c 100644
--- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
+++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java
@@ -25,6 +25,19 @@ public class RemoveBlockGoal extends MoveToBlockGoal {
private final Mob removerMob;
private int ticksSinceReachedGoal;
private static final int WAIT_AFTER_BLOCK_FOUND = 20;
+ // Anonymous start - Lithium Optimize remove block goal
+ private static final java.util.function.BiPredicate<ChunkAccess, BlockPos.MutableBlockPos> IS_VALID_TARGET_ABOVE_BIPREDICATE =
+ RemoveBlockGoal::lithium$isValidTargetAbove;
+ //Split check condition in order to use maybeHas
+ private boolean lithium$isValidTargetBlock(net.minecraft.world.level.block.state.BlockState blockState){
+ return blockState.is(this.blockToRemove);
+ }
+
+ private static boolean lithium$isValidTargetAbove(ChunkAccess chunkAccess, BlockPos.MutableBlockPos mutable) {
+ return chunkAccess.getBlockState(mutable.move(0, 1, 0)).isAir()
+ && chunkAccess.getBlockState(mutable.move(0, 1, 0)).isAir();
+ }
+ // Anonymous end - Lithium Optimize remove block goal
public RemoveBlockGoal(final Block blockToRemove, final PathfinderMob mob, final double speedModifier, final int verticalSearchRange) {
super(mob, speedModifier, 24, verticalSearchRange);
@@ -39,7 +52,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal {
} else if (this.nextStartTick > 0) {
this.nextStartTick--;
return false;
- } else if (this.findNearestBlock()) {
+ } else if (((net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.LithiumMoveToBlockGoal) this).lithium$findNearestBlock(this::lithium$isValidTargetBlock, IS_VALID_TARGET_ABOVE_BIPREDICATE, false)) { // Anonymous - Lithium Optimize remove block goal
this.nextStartTick = reducedTickDelay(20);
return true;
} else {
diff --git a/net/minecraft/world/entity/ai/sensing/HoglinSpecificSensor.java b/net/minecraft/world/entity/ai/sensing/HoglinSpecificSensor.java
index 7022679c84e184999b416e06d1af2cc2500cb12b..78743515ff2afd1ea1f06432f33f41a03fc7988b 100644
--- a/net/minecraft/world/entity/ai/sensing/HoglinSpecificSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/HoglinSpecificSensor.java
@@ -16,6 +16,14 @@ import net.minecraft.world.entity.monster.hoglin.Hoglin;
import net.minecraft.world.entity.monster.piglin.Piglin;
public class HoglinSpecificSensor extends Sensor<Hoglin> {
+ // Anonymous start - Lithium Optimizes Hoglin repellent search.
+ private static final java.util.function.Predicate<net.minecraft.world.level.block.state.BlockState> IS_VALID_REPELLENT_PREDICATE =
+ HoglinSpecificSensor::lithium$isValidRepellent;
+
+ private static boolean lithium$isValidRepellent(net.minecraft.world.level.block.state.BlockState blockState) {
+ return blockState.is(BlockTags.HOGLIN_REPELLENTS);
+ }
+ // Anonymous end - Lithium Optimizes Hoglin repellent search.
@Override
public Set<MemoryModuleType<?>> requires() {
return ImmutableSet.of(
@@ -30,8 +38,8 @@ public class HoglinSpecificSensor extends Sensor<Hoglin> {
@Override
protected void doTick(final ServerLevel level, final Hoglin body) {
- Brain<?> brain = body.getBrain();
- brain.setMemory(MemoryModuleType.NEAREST_REPELLENT, this.findNearestRepellent(level, body));
+ Brain<?> brain = body.getBrain();;
+ brain.setMemory(MemoryModuleType.NEAREST_REPELLENT, net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CommonBlockSearchesCheckAndCache.blockPosFindClosestMatch(level, body, 8, 4, IS_VALID_REPELLENT_PREDICATE, true)); // Anonymous - Lithium Optimizes Hoglin repellent search.
Optional<Piglin> adultPiglin = Optional.empty();
int adultPiglinCount = 0;
List<Hoglin> adultHoglins = Lists.newArrayList();
diff --git a/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java b/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java
index 060a53da799a3036a343cc6f1d73e3e5ca4b7af9..8594f559a5c8f5957348f7178464d5e9c5abc5ff 100644
--- a/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java
@@ -26,6 +26,15 @@ import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.state.BlockState;
public class PiglinSpecificSensor extends Sensor<LivingEntity> {
+ // Anonymous start - Lithium Optimizes Piglin repellent search.
+ private static final java.util.function.Predicate<BlockState> IS_VALID_REPELLENT_PREDICATE =
+ PiglinSpecificSensor::lithium$isValidRepellent;
+ private static boolean lithium$isValidRepellent(BlockState blockState){
+ final boolean isPiglinRepellent = blockState.is(BlockTags.PIGLIN_REPELLENTS);
+ return isPiglinRepellent && blockState.is(Blocks.SOUL_CAMPFIRE) ?
+ CampfireBlock.isLitCampfire(blockState) : isPiglinRepellent;
+ }
+ // Anonymous end - Lithium Optimizes Piglin repellent search.
@Override
public Set<MemoryModuleType<?>> requires() {
return ImmutableSet.of(
@@ -47,7 +56,7 @@ public class PiglinSpecificSensor extends Sensor<LivingEntity> {
@Override
protected void doTick(final ServerLevel level, final LivingEntity body) {
Brain<?> brain = body.getBrain();
- brain.setMemory(MemoryModuleType.NEAREST_REPELLENT, findNearestRepellent(level, body));
+ brain.setMemory(MemoryModuleType.NEAREST_REPELLENT, net.caffeinemc.mods.lithium.common.ai.non_poi_block_search.CommonBlockSearchesCheckAndCache.blockPosFindClosestMatch(level, body, 8, 4, IS_VALID_REPELLENT_PREDICATE, true)); // Anonymous - Lithium Optimizes Piglin repellent search.
Optional<Mob> nemesis = Optional.empty();
Optional<Hoglin> huntableHoglin = Optional.empty();
Optional<Hoglin> babyHoglin = Optional.empty();