Compare commits

...

1 Commits

Author SHA1 Message Date
Sofiane H. Djerbi
a3a6c53f66 Pathfinding fixes
Fix shulker boxes breaking pathfinding
Fix petal memory leak
Remove the thread queue
2023-08-05 02:59:48 +02:00

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Async path processing
diff --git a/src/main/java/dev/kaiijumc/kaiiju/KaiijuConfig.java b/src/main/java/dev/kaiijumc/kaiiju/KaiijuConfig.java
index ebfa9e1dcca5ea8272e796f0409902d92b59ee76..6be4abdd16f2d57a80dbe175a91ff304fd17a7db 100644
index 6df1720159383c2f536b40ded1092a437c1a20af..fc88b9f1e7e8f5858a91deeca2a5d51266a79a93 100644
--- a/src/main/java/dev/kaiijumc/kaiiju/KaiijuConfig.java
+++ b/src/main/java/dev/kaiijumc/kaiiju/KaiijuConfig.java
@@ -12,6 +12,7 @@ import org.bukkit.configuration.file.YamlConfiguration;
@@ -16,14 +16,13 @@ index ebfa9e1dcca5ea8272e796f0409902d92b59ee76..6be4abdd16f2d57a80dbe175a91ff304
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -213,12 +214,28 @@ public class KaiijuConfig {
@@ -218,12 +219,26 @@ public class KaiijuConfig {
public static boolean disablePlayerStats = false;
public static boolean disableArmSwingEvent = false;
public static boolean disableEnsureTickThreadChecks = false;
+ public static boolean asyncPathProcessing = false;
+ public static int asyncPathProcessingMaxThreads = 0;
+ public static int asyncPathProcessingKeepalive = 60;
+ public static int asyncPathProcessingQueueCapacity = 4096;
private static void optimizationSettings() {
disableVanishApi = getBoolean("optimization.disable-vanish-api", disableVanishApi);
@@ -33,7 +32,6 @@ index ebfa9e1dcca5ea8272e796f0409902d92b59ee76..6be4abdd16f2d57a80dbe175a91ff304
+ asyncPathProcessing = getBoolean("optimization.async-path-processing.enable", asyncPathProcessing);
+ asyncPathProcessingMaxThreads = getInt("optimization.async-path-processing.max-threads", asyncPathProcessingMaxThreads);
+ asyncPathProcessingKeepalive = getInt("optimization.async-path-processing.keepalive", asyncPathProcessingKeepalive);
+ asyncPathProcessingQueueCapacity = getInt("optimization.async-path-processing.queue-capacity", asyncPathProcessingQueueCapacity);
+ if (asyncPathProcessingMaxThreads < 0)
+ asyncPathProcessingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathProcessingMaxThreads, 1);
+ else if (asyncPathProcessingMaxThreads == 0)
@@ -340,10 +338,10 @@ index 0000000000000000000000000000000000000000..6b91852238f80d236fc44f766b115267
+}
diff --git a/src/main/java/dev/kaiijumc/kaiiju/path/AsyncPathProcessor.java b/src/main/java/dev/kaiijumc/kaiiju/path/AsyncPathProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6de8906d1629c60523c02681379ccdcaa22b588
index 0000000000000000000000000000000000000000..acdb210e706ea85355f971d73aed92b071410abf
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/path/AsyncPathProcessor.java
@@ -0,0 +1,53 @@
@@ -0,0 +1,48 @@
+package dev.kaiijumc.kaiiju.path;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -362,12 +360,7 @@ index 0000000000000000000000000000000000000000..a6de8906d1629c60523c02681379ccdc
+ */
+public class AsyncPathProcessor {
+
+ private static final Executor pathProcessingExecutor = new ThreadPoolExecutor(
+ 1,
+ dev.kaiijumc.kaiiju.KaiijuConfig.asyncPathProcessingMaxThreads,
+ dev.kaiijumc.kaiiju.KaiijuConfig.asyncPathProcessingKeepalive,
+ TimeUnit.SECONDS,
+ new ArrayBlockingQueue<>(dev.kaiijumc.kaiiju.KaiijuConfig.asyncPathProcessingQueueCapacity),
+ private static final Executor pathProcessingExecutor = Executors.newCachedThreadPool(
+ new ThreadFactoryBuilder()
+ .setNameFormat("petal-path-processor-%d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
@@ -399,10 +392,10 @@ index 0000000000000000000000000000000000000000..a6de8906d1629c60523c02681379ccdc
+}
diff --git a/src/main/java/dev/kaiijumc/kaiiju/path/NodeEvaluatorCache.java b/src/main/java/dev/kaiijumc/kaiiju/path/NodeEvaluatorCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..121eda164714650f76bb6c8495ef375d8a00d812
index 0000000000000000000000000000000000000000..3213fed7cea3ebfc364f4d6603b95f4263222c76
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/path/NodeEvaluatorCache.java
@@ -0,0 +1,42 @@
@@ -0,0 +1,45 @@
+package dev.kaiijumc.kaiiju.path;
+
+import net.minecraft.world.level.pathfinder.NodeEvaluator;
@@ -419,13 +412,13 @@ index 0000000000000000000000000000000000000000..121eda164714650f76bb6c8495ef375d
+ private static final Map<NodeEvaluatorFeatures, ConcurrentLinkedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
+ private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
+
+ private static @NotNull Queue<NodeEvaluator> getDequeForGenerator(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
+ private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
+ return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, (key) -> new ConcurrentLinkedQueue<>());
+ }
+
+ public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator);
+ NodeEvaluator nodeEvaluator = getDequeForGenerator(nodeEvaluatorFeatures).poll();
+ NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll();
+
+ if (nodeEvaluator == null) {
+ nodeEvaluator = generator.generate(nodeEvaluatorFeatures);
@@ -437,13 +430,16 @@ index 0000000000000000000000000000000000000000..121eda164714650f76bb6c8495ef375d
+ }
+
+ public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
+ final var generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
+ final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
+ Validate.notNull(generator, "NodeEvaluator already returned");
+
+ getDequeForGenerator(nodeEvaluatorFeatures).offer(nodeEvaluator);
+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
+ getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator);
+ }
+
+ public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
+ nodeEvaluatorToGenerator.remove(nodeEvaluator);
+ }
+}
diff --git a/src/main/java/dev/kaiijumc/kaiiju/path/NodeEvaluatorFeatures.java b/src/main/java/dev/kaiijumc/kaiiju/path/NodeEvaluatorFeatures.java
new file mode 100644
@@ -1171,10 +1167,10 @@ index 2a335f277bd0e4b8ad0f60d8226eb8aaa80a871f..b2c3c459fae7d0cb5ef0fcbc2ff0e61c
return false;
} else if (o.nodes.size() != this.nodes.size()) {
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f11a91304 100644
index d23481453717f715124156b5d83f6448f720d049..bab740b11e4cc20f924fcf9e33779cdc811b6e8f 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -24,37 +24,77 @@ public class PathFinder {
@@ -24,37 +24,82 @@ public class PathFinder {
public final NodeEvaluator nodeEvaluator;
private static final boolean DEBUG = false;
private final BinaryHeap openSet = new BinaryHeap();
@@ -1207,11 +1203,7 @@ index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f
+ Node node = nodeEvaluator.getStart();
+ // Kaiiju end
if (node == null) {
+ // Kaiiju start - petal - handle nodeEvaluatorGenerator
+ if (this.nodeEvaluatorGenerator != null) {
+ dev.kaiijumc.kaiiju.path.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
+ }
+ // Kaiiju end
+ dev.kaiijumc.kaiiju.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // Kaiiju - petal - handle nodeEvaluatorGenerator
return null;
} else {
// Paper start - remove streams - and optimize collection
@@ -1227,13 +1219,22 @@ index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f
+ // Kaiiju start - petal - async path processing
+ if (this.nodeEvaluatorGenerator == null) {
+ // run sync :(
+ return this.findPath(nodeEvaluator, world.getProfiler(), node, map, followRange, distance, rangeMultiplier);
+ dev.kaiijumc.kaiiju.path.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator);
+ return this.findPath(world.getProfiler(), node, map, followRange, distance, rangeMultiplier);
+ }
+
+ if (mob != null && mob.hasCustomName() && mob.getCustomName().getString().contains("1o")) org.bukkit.Bukkit.getLogger().info("Positions " + positions);
+ return new dev.kaiijumc.kaiiju.path.AsyncPath(Lists.newArrayList(), positions, () -> {
+ try {
+ return this.processPath(nodeEvaluator, node, map, followRange, distance, rangeMultiplier);
+ Path path = this.processPath(nodeEvaluator, node, map, followRange, distance, rangeMultiplier);
+ if (mob != null && mob.hasCustomName() && mob.getCustomName().getString().contains("1o")) org.bukkit.Bukkit.getLogger().info("path " + path);
+ return path;
+ } catch (Exception e) {
+ if (mob != null && mob.hasCustomName() && mob.getCustomName().getString().contains("1o")) org.bukkit.Bukkit.getLogger().info("error path: " + e);
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (mob != null && mob.hasCustomName() && mob.getCustomName().getString().contains("1o")) org.bukkit.Bukkit.getLogger().info("NE " + nodeEvaluator);
+ nodeEvaluator.done();
+ dev.kaiijumc.kaiiju.path.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
+ }
@@ -1245,24 +1246,23 @@ index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f
- @Nullable
+ //@Nullable // Kaiiju - Always not null
// Paper start - optimize collection
- private Path findPath(ProfilerFiller profiler, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) {
+ private Path findPath(NodeEvaluator nodeEvaluator, ProfilerFiller profiler, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) {
private Path findPath(ProfilerFiller profiler, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) {
profiler.push("find_path");
profiler.markForCharting(MetricCategory.PATH_FINDING);
+ // Kaiiju start - petal - split pathfinding into the original sync method for compat and processing for delaying
+ try {
+ return this.processPath(this.nodeEvaluator, startNode, positions, followRange, distance, rangeMultiplier);
+ } finally {
+ nodeEvaluator.done();
+ this.nodeEvaluator.done();
+ }
+ }
+ private synchronized Path processPath(NodeEvaluator nodeEvaluator, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) { // sync to only use the caching functions in this class on a single thread
+ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) { // 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 = positions.keySet();
startNode.g = 0.0F;
startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection
@@ -91,7 +131,7 @@ public class PathFinder {
@@ -91,7 +136,7 @@ public class PathFinder {
}
if (!(node.distanceTo(startNode) >= followRange)) {
@@ -1271,6 +1271,14 @@ index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f
for(int l = 0; l < k; ++l) {
Node node2 = this.neighbors[l];
@@ -123,6 +168,7 @@ public class PathFinder {
if (best == null || comparator.compare(path, best) < 0)
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
}
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java
index 0e2b14e7dfedf209d63279c81723fd7955122d78..079b278e2e262af433bb5bd0c12b3d8db4fa12fc 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java