9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-26 10:29:13 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0113-Petal-Async-Pathfinding.patch
Dreeam 9a4efaa230 Drop patch that causes performance regression
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2
since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList.
Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2
lazy handles filter condition on iteration, so much better.
2025-08-04 19:25:56 +08:00

866 lines
49 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: peaches94 <peachescu94@gmail.com>
Date: Sun, 26 Jun 2022 16:51:37 -0500
Subject: [PATCH] Petal: Async Pathfinding
Fixed & Updated by KaiijuMC
Original license: GPLv3
Original project: https://github.com/KaiijuMC/Kaiiju
Original license: GPLv3
Original project: https://github.com/Bloom-host/Petal
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Co-authored-by: Taiyou06 <kaandindar21@gmail.com>
Co-authored-by: Altiami <yoshimo.kristin@gmail.com>
This patch was ported downstream from the Petal fork.
Makes most pathfinding-related work happen asynchronously
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 6aed8d7c737a35943718535226cedd65f86d3b3c..ec50760ec81728a0b8ab8d691ed65ba6d25d32f9 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -247,6 +247,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
@Nullable
@Override
public LivingEntity getTarget() {
+ //if (Thread.currentThread().getName().contains("petal-async-pathfinding-thread")) return this.target; // Kaiiju - Don't reset target when async pathfinding! // Leaf - Don't need this
return this.target;
}
diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 7f0975f8bd6d5f8ca28f503f93c8cb5c42557420..698d7a690f012da87295f0d9035dc0e5f0626fcc 100644
--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -102,21 +102,20 @@ public class AcquirePoi {
}
}
// Paper end - optimise POI access
- Path path = findPathToPois(mob, set);
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- poiManager.getType(target).ifPresent(holder -> {
- poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
- memoryAccessor.set(GlobalPos.of(level.dimension(), target));
- entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
- map.clear();
- DebugPackets.sendPoiTicketCountPacket(level, target);
+ // Kaiiju start - petal - Async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ // await on path async
+ Path possiblePath = findPathToPois(mob, set);
+
+ // wait on the path to be processed
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(acquirablePois, entityEventId, map, memoryAccessor, level, mob, time, poiManager, set, path);
});
} else {
- for (Pair<Holder<PoiType>, BlockPos> pair : set) {
- map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time));
- }
+ Path path = findPathToPois(mob, set);
+ processPath(acquirablePois, entityEventId, map, memoryAccessor, level, mob, time, poiManager, set, path);
}
+ // Kaiiju end - Async path processing
return true;
}
@@ -128,6 +127,34 @@ public class AcquirePoi {
: BehaviorBuilder.create(instance -> instance.group(instance.absent(existingAbsentMemory)).apply(instance, memoryAccessor -> oneShot));
}
+ // Leaf start - Kaiiju - Async path processing
+ private static void processPath(Predicate<Holder<PoiType>> acquirablePois,
+ Optional<Byte> entityEventId,
+ Long2ObjectMap<JitteredLinearRetry> map,
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, GlobalPos> memoryAccessor,
+ ServerLevel level,
+ PathfinderMob mob,
+ long time,
+ PoiManager poiManager,
+ Set<Pair<Holder<PoiType>, BlockPos>> set,
+ @org.jetbrains.annotations.Nullable Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ poiManager.getType(target).ifPresent(holder -> {
+ poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
+ memoryAccessor.set(GlobalPos.of(level.dimension(), target));
+ entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
+ map.clear();
+ DebugPackets.sendPoiTicketCountPacket(level, target);
+ });
+ } else {
+ for (Pair<Holder<PoiType>, BlockPos> pair : set) {
+ map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time));
+ }
+ }
+ }
+ // Leaf end - Kaiiju - Async path processing
+
@Nullable
public static Path findPathToPois(Mob mob, Set<Pair<Holder<PoiType>, BlockPos>> poiPositions) {
if (poiPositions.isEmpty()) {
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index 621ba76784f2b92790eca62be4d0688834335ab6..e2e1532d2ffb709e347db42b1b5b6cae5e7e9700 100644
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
@@ -21,6 +21,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
private int remainingCooldown;
@Nullable
private Path path;
+ private boolean finishedProcessing; // Kaiiju - petal - track when path is processed
@Nullable
private BlockPos lastTargetPos;
private float speedModifier;
@@ -53,9 +54,10 @@ public class MoveToTargetSink extends Behavior<Mob> {
Brain<?> brain = owner.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean flag = this.reachedTarget(owner, walkTarget);
- if (!flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) {
+ if (!org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) { // Kaiiju - petal - async path processing means we can't know if the path is reachable here
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
return true;
+ } else if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !flag) { return true; // Kaiiju - async pathfinding
} else {
brain.eraseMemory(MemoryModuleType.WALK_TARGET);
if (flag) {
@@ -69,6 +71,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected boolean canStillUse(ServerLevel level, Mob entity, long gameTime) {
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && !this.finishedProcessing) return true; // Kaiiju - petal - wait for processing
if (this.path != null && this.lastTargetPos != null) {
Optional<WalkTarget> memory = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET);
boolean flag = memory.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false);
@@ -95,12 +98,68 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected void start(ServerLevel level, Mob entity, long gameTime) {
+ // Kaiiju start - petal - start processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ Brain<?> brain = entity.getBrain();
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
+
+ this.finishedProcessing = false;
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ this.path = this.computePath(entity, walkTarget);
+ return;
+ }
+ // Kaiiju end
entity.getBrain().setMemory(MemoryModuleType.PATH, this.path);
entity.getNavigation().moveTo(this.path, (double)this.speedModifier);
}
@Override
protected void tick(ServerLevel level, Mob owner, long gameTime) {
+ // Kaiiju start - petal - Async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ if (this.path != null && !this.path.isProcessed()) return; // wait for processing
+
+ if (!this.finishedProcessing) {
+ this.finishedProcessing = true;
+
+ Brain<?> brain = owner.getBrain();
+ boolean canReach = this.path != null && this.path.canReach();
+ if (canReach) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) {
+ brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, gameTime);
+ }
+
+ if (!canReach) {
+ Optional<WalkTarget> walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET);
+
+ if (!walkTarget.isPresent()) return;
+
+ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition();
+ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob) owner, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F);
+ if (vec3 != null) {
+ // try recalculating the path using a random position
+ this.path = owner.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
+ this.finishedProcessing = false;
+ return;
+ }
+ }
+
+ owner.getBrain().setMemory(MemoryModuleType.PATH, this.path);
+ owner.getNavigation().moveTo(this.path, this.speedModifier);
+ }
+
+ Path path = owner.getNavigation().getPath();
+ Brain<?> brain = owner.getBrain();
+
+ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); // we know isPresent = true
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) {
+ this.start(level, owner, gameTime);
+ }
+ }
+ } else {
+ // Kaiiju end
Path path = owner.getNavigation().getPath();
Brain<?> brain = owner.getBrain();
if (this.path != path) {
@@ -115,7 +174,23 @@ public class MoveToTargetSink extends Behavior<Mob> {
this.start(level, owner, gameTime);
}
}
+ } // Kaiiju - async path processing
+ }
+
+ // Kaiiju start - petal - Async path processing
+ @Nullable
+ private Path computePath(Mob entity, WalkTarget walkTarget) {
+ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition();
+ // don't pathfind outside region
+ //if (!io.papermc.paper.util.TickThread.isTickThreadFor((ServerLevel) entity.level(), blockPos)) return null; // Leaf - Don't need this
+ this.speedModifier = walkTarget.getSpeedModifier();
+ Brain<?> brain = entity.getBrain();
+ if (this.reachedTarget(entity, walkTarget)) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ }
+ return entity.getNavigation().createPath(blockPos, 0);
}
+ // Kaiiju end
private boolean tryComputePath(Mob mob, WalkTarget target, long time) {
BlockPos blockPos = target.getTarget().currentBlockPosition();
diff --git a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
index 4f9f3367b1ca3903df03a80fa2b01a3d24e6e77d..51413df5cd61b3ff59c6c6c3ec69d6732ab07d83 100644
--- a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
+++ b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
@@ -60,17 +60,20 @@ public class SetClosestHomeAsWalkTarget {
poi -> poi.is(PoiTypes.HOME), predicate, mob.blockPosition(), 48, PoiManager.Occupancy.ANY
)
.collect(Collectors.toSet());
+ // Kaiiju start - petal - Async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ // await on path async
+ Path possiblePath = AcquirePoi.findPathToPois(mob, set);
+
+ // wait on the path to be processed
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
+ });
+ } else {
+ // Kaiiju end
Path path = AcquirePoi.findPathToPois(mob, set);
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- Optional<Holder<PoiType>> type = poiManager.getType(target);
- if (type.isPresent()) {
- walkTarget.set(new WalkTarget(target, speedModifier, 1));
- DebugPackets.sendPoiTicketCountPacket(level, target);
- }
- } else if (mutableInt.getValue() < 5) {
- map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
- }
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
+ } // Kaiiju - async path processing
return true;
} else {
@@ -81,4 +84,26 @@ public class SetClosestHomeAsWalkTarget {
)
);
}
+
+ // Leaf start - Kaiiju - petal - Async path processing
+ private static void processPath(float speedModifier,
+ Long2LongMap map,
+ MutableLong mutableLong,
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, WalkTarget> walkTarget,
+ net.minecraft.server.level.ServerLevel level,
+ PoiManager poiManager,
+ MutableInt mutableInt,
+ @org.jetbrains.annotations.Nullable Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
+ if (type.isPresent()) {
+ walkTarget.set(new WalkTarget(target, speedModifier, 1));
+ DebugPackets.sendPoiTicketCountPacket(level, target);
+ }
+ } else if (mutableInt.getValue() < 5) {
+ map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
+ }
+ }
+ // Leaf end - Kaiiju - petal - Async path processing
}
diff --git a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
index 73bba480f3f017a8aed14562bd82ba33db04391c..ed73a102d1ca58d0c2d4ea6d715511284fa90896 100644
--- a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
+++ b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
@@ -54,7 +54,7 @@ public abstract class DoorInteractGoal extends Goal {
return false;
} else {
Path path = this.mob.getNavigation().getPath();
- if (path != null && !path.isDone()) {
+ if (path != null && path.isProcessed() && !path.isDone()) { // Kaiiju - async pathfinding - ensure path is processed
for (int i = 0; i < Math.min(path.getNextNodeIndex() + 2, path.getNodeCount()); i++) {
Node node = path.getNode(i);
this.doorPos = new BlockPos(node.x, node.y + 1, node.z);
diff --git a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
index 458ceec68ca138b0aa9b70d6c934473c01d468f4..dc42e63d1c166aed02081e687ada76a0e74da7a7 100644
--- a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation {
super(mob, level);
}
+ // Kaiiju start - petal - async path processing
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ AmphibiousNodeEvaluator nodeEvaluator = new AmphibiousNodeEvaluator(false);
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new AmphibiousNodeEvaluator(false);
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
index 077863b758fbc3e51f25bcf842d00a2cc07c6a2f..29490cf3cb3c42dbc1dff683d8942a40e2df71b6 100644
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -16,9 +16,25 @@ public class FlyingPathNavigation extends PathNavigation {
super(mob, level);
}
+ // Kaiiju start - petal - async path processing
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ FlyNodeEvaluator nodeEvaluator = new FlyNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new FlyNodeEvaluator();
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
@@ -48,6 +64,7 @@ public class FlyingPathNavigation extends PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // Kaiiju - petal - async path processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
diff --git a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
index 86fccf3617a32f3791b03d8067e2eaf6b8d8bebb..9cef6797e6d8eb43a51a1b94fea3947dd3d534da 100644
--- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
@@ -24,9 +24,25 @@ public class GroundPathNavigation extends PathNavigation {
super(mob, level);
}
+ // Kaiiju start - petal - async path processing
+ protected static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 24dd92449f70144c79f25bf24942ebd666655ed2..5060a4051c251e59f36843d118aeee891b256dac 100644
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -167,6 +167,10 @@ public abstract class PathNavigation {
return null;
} else if (!this.canUpdatePath()) {
return null;
+ // Kaiiju start - petal - catch early if it's still processing these positions let it keep processing
+ } else if (this.path instanceof org.dreeam.leaf.async.path.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(targets)) {
+ return this.path;
+ // Kaiiju end
} else if (this.path != null && !this.path.isDone() && targets.contains(this.targetPos)) {
return this.path;
} else {
@@ -191,11 +195,29 @@ public abstract class PathNavigation {
int i = (int)(followRange + regionOffset);
PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i));
Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, targets, followRange, accuracy, this.maxVisitedNodesMultiplier);
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ // assign early a target position. most calls will only have 1 position
+ if (!targets.isEmpty()) this.targetPos = targets.iterator().next();
+
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(path, processedPath -> {
+ // check that processing didn't take so long that we calculated a new path
+ if (processedPath != this.path) return;
+
+ if (processedPath != null && processedPath.getTarget() != null) {
+ this.targetPos = processedPath.getTarget();
+ this.reachRange = accuracy;
+ this.resetStuckTimeout();
+ }
+ });
+ } else {
+ // Kaiiju end
if (path != null && path.getTarget() != null) {
this.targetPos = path.getTarget();
this.reachRange = accuracy;
this.resetStuckTimeout();
}
+ } // Kaiiju - async path processing
return path;
}
@@ -246,8 +268,8 @@ public abstract class PathNavigation {
if (this.isDone()) {
return false;
} else {
- this.trimPath();
- if (this.path.getNodeCount() <= 0) {
+ if (path.isProcessed()) this.trimPath(); // Kaiiju - petal - only trim if processed
+ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // Kaiiju - petal - only check node count if processed
return false;
} else {
this.speedModifier = speed;
@@ -270,6 +292,7 @@ public abstract class PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // Kaiiju - petal - skip pathfinding if we're still processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
@@ -299,6 +322,7 @@ public abstract class PathNavigation {
}
protected void followThePath() {
+ if (!this.path.isProcessed()) return; // Kaiiju - petal - skip if not processed
Vec3 tempMobPos = this.getTempMobPos();
this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F;
Vec3i nextNodePos = this.path.getNextNodePos();
@@ -455,7 +479,7 @@ public abstract class PathNavigation {
public boolean shouldRecomputePath(BlockPos pos) {
if (this.hasDelayedRecomputation) {
return false;
- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
+ } else if (this.path != null && this.path.isProcessed() && !this.path.isDone() && this.path.getNodeCount() != 0) { // Kaiiju - petal - Skip if not processed
Node endNode = this.path.getEndNode();
Vec3 vec3 = new Vec3((endNode.x + this.mob.getX()) / 2.0, (endNode.y + this.mob.getY()) / 2.0, (endNode.z + this.mob.getZ()) / 2.0);
return pos.closerToCenterThan(vec3, this.path.getNodeCount() - this.path.getNextNodeIndex());
diff --git a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
index ea0f6a19e4a79538e68917ba86cbc98be4dbca8d..e2ed5b1d9eca79dc5a63d70d718fdf8298969e97 100644
--- a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation {
super(mob, level);
}
+ // Kaiiju start - petal - async path processing
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ SwimNodeEvaluator nodeEvaluator = new SwimNodeEvaluator(nodeEvaluatorFeatures.allowBreaching());
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN;
this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching);
this.nodeEvaluator.setCanPassDoors(false);
+ // Kaiiju start - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 1f96fd5085bacb4c584576c7cb9f51e7898e9b03..03b6c8c8dcd42e864751e68be9d35d2045468655 100644
--- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -57,17 +57,32 @@ public class NearestBedSensor extends Sensor<Mob> {
java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
// don't ask me why it's unbounded. ask mojang.
io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), level.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur - Configurable villager search radius
- Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
// Paper end - optimise POI access
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- Optional<Holder<PoiType>> type = poiManager.getType(target);
- if (type.isPresent()) {
- entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
- }
- } else if (this.triedCount < 5) {
- this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
+ // Kaiiju start - await on async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ org.dreeam.leaf.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(entity, poiManager, path);
+ });
+ } else {
+ // Kaiiju end
+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ processPath(entity, poiManager, path);
+ } // Kaiiju - async path processing
+ }
+ }
+
+ // Leaf start - Kaiiju - await on async path processing
+ private void processPath(Mob entity, PoiManager poiManager, @org.jetbrains.annotations.Nullable Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
+ if (type.isPresent()) {
+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
}
+ } else if (this.triedCount < 5) {
+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
}
}
+ // Leaf end - Kaiiju - await on async path processing
}
diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java
index 7573f1be88ca23096e02efe2fa933429fa4421ba..4821ec280cbd6f4b19241748c3bcc3fb66149ac2 100644
--- a/net/minecraft/world/entity/animal/Bee.java
+++ b/net/minecraft/world/entity/animal/Bee.java
@@ -938,6 +938,14 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
Bee.this.pathfindRandomlyTowards(Bee.this.hivePos);
}
} else {
+ // Leaf start - async path processing fix
+ // Wait for path processing before making navigation decisions
+ Path currentPath = Bee.this.navigation.getPath();
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled && currentPath != null && !currentPath.isProcessed()) {
+ return;
+ }
+ // Leaf end - async path processing fix
+
boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos);
if (!flag) {
this.dropAndBlacklistHive();
@@ -991,7 +999,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
return true;
} else {
Path path = Bee.this.navigation.getPath();
- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
+ return path != null && path.isProcessed() && path.getTarget().equals(pos) && path.canReach() && path.isDone(); // Kaiiju - petal - ensure path is processed
}
}
}
diff --git a/net/minecraft/world/entity/animal/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java
index 1af1b33702296f9aa74c33436ea2904c5e5ae43d..041ccb96d36cbe0f5683ff6e8b2adb0b79a96738 100644
--- a/net/minecraft/world/entity/animal/Rabbit.java
+++ b/net/minecraft/world/entity/animal/Rabbit.java
@@ -612,6 +612,14 @@ public class Rabbit extends Animal {
@Override
public void vanillaTick() { // Purpur - Ridables
+ // Leaf start - async pathfinding - don't make movement decisions with unprocessed paths
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ Path path = this.rabbit.getNavigation().getPath();
+ if (path != null && !path.isProcessed()) {
+ return; // Wait for path processing before making movement decisions
+ }
+ }
+ // Leaf end - async pathfinding
if (this.rabbit.onGround() && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl)this.rabbit.jumpControl).wantJump()) {
this.rabbit.setSpeedModifier(0.0);
} else if (this.hasWanted() || this.operation == MoveControl.Operation.JUMPING) {
diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java
index a7e9179c287671a689cb5ce668b5a33896da87a4..7d450b6ccde99657a7fac354ae386a80f9f7e879 100644
--- a/net/minecraft/world/entity/animal/frog/Frog.java
+++ b/net/minecraft/world/entity/animal/frog/Frog.java
@@ -489,9 +489,25 @@ public class Frog extends Animal {
return pathType != PathType.WATER_BORDER && super.canCutCorner(pathType);
}
+ // Kaiiju start - petal - async path processing
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ Frog.FrogNodeEvaluator nodeEvaluator = new Frog.FrogNodeEvaluator(true);
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new Frog.FrogNodeEvaluator(true);
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
}
diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java
index 2e6d0f035a01277aa28bbe912d5df8dc4cf04547..f4428016e4fabc99dc79ecc39b7f6e0617e4dc7a 100644
--- a/net/minecraft/world/entity/monster/Drowned.java
+++ b/net/minecraft/world/entity/monster/Drowned.java
@@ -308,7 +308,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
protected boolean closeToNextPos() {
Path path = this.getNavigation().getPath();
- if (path != null) {
+ if (path != null && path.isProcessed()) { // Kaiiju - petal - ensure path is processed
BlockPos target = path.getTarget();
if (target != null) {
double d = this.distanceToSqr(target.getX(), target.getY(), target.getZ());
diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java
index fe31c4a45afd61be8b74efe9d0858ccd0aced075..e1717b5c854aa81fdd7b7e715d7c3498d9f86072 100644
--- a/net/minecraft/world/entity/monster/Strider.java
+++ b/net/minecraft/world/entity/monster/Strider.java
@@ -560,9 +560,25 @@ public class Strider extends Animal implements ItemSteerable {
super(strider, level);
}
+ // Kaiiju start - petal - async path processing
+ private static final org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.dreeam.leaf.async.path.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // Kaiiju end
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // Kaiiju start - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java
index 147fb3eecf651dcee63d63255542ef2680140f66..a6109ac9d7f8a9aa4074b3e9504244b1d565a2d4 100644
--- a/net/minecraft/world/entity/monster/warden/Warden.java
+++ b/net/minecraft/world/entity/monster/warden/Warden.java
@@ -576,6 +576,16 @@ public class Warden extends Monster implements VibrationSystem {
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // Kaiiju start - petal - async path processing
+ if (org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, GroundPathNavigation.nodeEvaluatorGenerator) {
+ @Override
+ protected float distance(Node first, Node second) {
+ return first.distanceToXZ(second);
+ }
+ };
+ }
+ // Kaiiju end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes) {
@Override
protected float distance(Node first, Node second) {
diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java
index 49bac7af90b0a7c490141be6357563447783c6ca..8aedb5497ea52ae59e477162cb8089694a17f091 100644
--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java
+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java
@@ -166,9 +166,16 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
+ //if (Thread.currentThread().getName().contains("petal-async-pathfinding-thread")) return Shapes.block(); // Kaiiju - async pathfinding - we cannot get block entities // Leaf - Don't need this
return level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity
? Shapes.create(shulkerBoxBlockEntity.getBoundingBox(state))
: Shapes.block();
+ // Kaiiju start - async pathfinding - workaround // Leaf - Don't need this
+ /*
+ } catch (NullPointerException e) {
+ return Shapes.block();
+ }
+ */
}
@Override
diff --git a/net/minecraft/world/level/pathfinder/Path.java b/net/minecraft/world/level/pathfinder/Path.java
index d6d3c8f5e5dd4a8cab0d3fcc131c3a59f06130c6..add5b8b98e4d09617cbd4e7dd2710dc50781613a 100644
--- a/net/minecraft/world/level/pathfinder/Path.java
+++ b/net/minecraft/world/level/pathfinder/Path.java
@@ -26,6 +26,17 @@ public class Path {
this.reached = reached;
}
+ // Kaiiju start - petal - async path processing
+ /**
+ * checks if the path is completely processed in the case of it being computed async
+ *
+ * @return true if the path is processed
+ */
+ public boolean isProcessed() {
+ return true;
+ }
+ // Kaiiju end
+
public void advance() {
this.nextNodeIndex++;
}
@@ -99,6 +110,7 @@ public class Path {
}
public boolean sameAs(@Nullable Path pathentity) {
+ if (pathentity == this) return true; // Kaiiju - petal - short circuit
if (pathentity == null) {
return false;
} else if (pathentity.nodes.size() != this.nodes.size()) {
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
index c2baadcdceb1df6a881d6f73aa4eb4dd264bcdfe..61912c67611ded5a8f34e0c55d3d3017f144f970 100644
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -22,10 +22,18 @@ public class PathFinder {
public final NodeEvaluator nodeEvaluator;
private static final boolean DEBUG = false;
private final BinaryHeap openSet = new BinaryHeap();
+ private final @Nullable org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator; // Kaiiju - petal - we use this later to generate an evaluator
- public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes, @Nullable org.dreeam.leaf.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator) { // Kaiiju - petal - add nodeEvaluatorGenerator
this.nodeEvaluator = nodeEvaluator;
this.maxVisitedNodes = maxVisitedNodes;
+ // Kaiiju start - petal - support nodeEvaluatorgenerators
+ this.nodeEvaluatorGenerator = nodeEvaluatorGenerator;
+ }
+
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
+ this(nodeEvaluator, maxVisitedNodes, null);
+ // Kaiiju end
}
public void setMaxVisitedNodes(int maxVisitedNodes) {
@@ -34,26 +42,63 @@ public class PathFinder {
@Nullable
public Path findPath(PathNavigationRegion region, Mob mob, Set<BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
- this.openSet.clear();
- this.nodeEvaluator.prepare(region, mob);
- Node start = this.nodeEvaluator.getStart();
+ if (!org.dreeam.leaf.config.modules.async.AsyncPathfinding.enabled)
+ this.openSet.clear(); // Kaiiju - petal - it's always cleared in processPath
+ // Kaiiju start - petal - use a generated evaluator if we have one otherwise run sync
+ NodeEvaluator nodeEvaluator = this.nodeEvaluatorGenerator == null
+ ? this.nodeEvaluator
+ : org.dreeam.leaf.async.path.NodeEvaluatorCache.takeNodeEvaluator(this.nodeEvaluatorGenerator, this.nodeEvaluator);
+ nodeEvaluator.prepare(region, mob);
+ Node start = nodeEvaluator.getStart();
+ // Kaiiju end
if (start == null) {
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // Kaiiju - petal - handle nodeEvaluatorGenerator
return null;
} else {
// Paper start - Perf: remove streams and optimize collection
List<Map.Entry<Target, BlockPos>> map = Lists.newArrayList();
for (BlockPos pos : targetPositions) {
- map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
+ map.add(new java.util.AbstractMap.SimpleEntry<>(nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); // Kaiiju - petal - handle nodeEvaluatorGenerator
}
// Paper end - Perf: remove streams and optimize collection
- Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
- this.nodeEvaluator.done();
- return path;
+ // Kaiiju start - petal - async path processing
+ if (this.nodeEvaluatorGenerator == null) {
+ // run sync :(
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator);
+ return this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
+ }
+
+ return new org.dreeam.leaf.async.path.AsyncPath(Lists.newArrayList(), targetPositions, () -> {
+ try {
+ return this.processPath(nodeEvaluator, start, map, maxRange, accuracy, searchDepthMultiplier);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ nodeEvaluator.done();
+ org.dreeam.leaf.async.path.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
+ }
+ });
+ // Kaiiju end
}
}
@Nullable
private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
+ // Kaiiju start - petal - split pathfinding into the original sync method for compat and processing for delaying
+ try {
+ return this.processPath(this.nodeEvaluator, node, positions, maxRange, accuracy, searchDepthMultiplier);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ this.nodeEvaluator.done();
+ }
+ }
+
+ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // sync to only use the caching functions in this class on a single thread
+ org.apache.commons.lang3.Validate.isTrue(!positions.isEmpty()); // ensure that we have at least one position, which means we'll always return a path
+ // Kaiiju end
// Set<Target> set = targetPositions.keySet(); // Paper
node.g = 0.0F;
node.h = this.getBestH(node, positions); // Paper - optimize collection
@@ -89,7 +134,7 @@ public class PathFinder {
}
if (!(node1.distanceTo(node) >= maxRange)) {
- int neighbors = this.nodeEvaluator.getNeighbors(this.neighbors, node1);
+ int neighbors = nodeEvaluator.getNeighbors(this.neighbors, node1); // Kaiiju - petal - use provided nodeEvaluator
for (int i2 = 0; i2 < neighbors; i2++) {
Node node2 = this.neighbors[i2];
@@ -123,6 +168,7 @@ public class PathFinder {
best = path;
}
}
+ //noinspection ConstantConditions // Kaiiju - petal - ignore this warning, we know that the above loop always runs at least once since positions is not empty
return best;
// Paper end - Perf: remove streams and optimize collection
}