188 lines
11 KiB
Diff
188 lines
11 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|||
|
|
From: NanaChiyo0721 <nanachiyo0721@163.com>
|
||
|
|
Date: Sat, 25 Jul 2026 21:30:17 +0800
|
||
|
|
Subject: [PATCH] Lithium Optimizes move to block goal
|
||
|
|
|
||
|
|
|
||
|
|
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||
|
|
index c6e9b155c2341c13d569f28f54f0c1d2d7313676..7bd3c8a1c627cfd3e377a269c649c0c169634b49 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 { // Shiroha - 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);
|
||
|
|
+ // Shiorha 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;
|
||
|
|
+ }
|
||
|
|
+ // Shiorha end - Lithium Optimizes move to block goal
|
||
|
|
}
|