Skip to content

Commit

Permalink
Pathfinding fixes
Browse files Browse the repository at this point in the history
Fix shulker boxes breaking pathfinding
Fix petal memory leak
Remove the thread queue
  • Loading branch information
sofianedjerbi committed Aug 5, 2023
1 parent eb6be9f commit dc49313
Showing 1 changed file with 58 additions and 33 deletions.
91 changes: 58 additions & 33 deletions patches/server/0043-Async-path-processing.patch
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -340,15 +338,15 @@ 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..8ac99ae5f0c0fc8a471be293c9033c04ec644780
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/path/AsyncPathProcessor.java
@@ -0,0 +1,53 @@
@@ -0,0 +1,52 @@
+package dev.kaiijumc.kaiiju.path;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import net.minecraft.world.level.pathfinder.Path;
+import dev.kaiijumc.kaiiju.KaiijuConfig;import net.minecraft.world.level.pathfinder.Path;
+import net.minecraft.world.entity.Entity;
+
+import org.jetbrains.annotations.NotNull;
Expand All @@ -364,14 +362,13 @@ index 0000000000000000000000000000000000000000..a6de8906d1629c60523c02681379ccdc
+
+ 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),
+ new ThreadFactoryBuilder()
+ .setNameFormat("petal-path-processor-%d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
+ .build()
+ KaiijuConfig.asyncPathProcessingMaxThreads,
+ KaiijuConfig.asyncPathProcessingKeepalive, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new ThreadFactoryBuilder()
+ .setNameFormat("petal-path-processor-%d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
+ .build()
+ );
+
+ protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
Expand Down Expand Up @@ -399,10 +396,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;
Expand All @@ -419,13 +416,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);
Expand All @@ -437,13 +434,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
Expand Down Expand Up @@ -1140,6 +1140,24 @@ index 97b763431bc5015448ee7a26a340635a932c950b..48109aebe34cbdfac3eceffb1c20aa84
return new PathFinder(this.nodeEvaluator, range) {
@Override
protected float distance(Node a, Node b) {
diff --git a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
index b51155ad12515b2d0dd0f202580b9f455c114d9a..358233cb35860adad86c01872c65ec6f165ba594 100644
--- a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
@@ -242,8 +242,13 @@ public class ShulkerBoxBlock extends BaseEntityBlock {

@Override
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
+ try { // Kaiiju - async pathfinding - lol i hate that
BlockEntity blockEntity = world.getBlockEntity(pos);
return blockEntity instanceof ShulkerBoxBlockEntity ? Shapes.create(((ShulkerBoxBlockEntity)blockEntity).getBoundingBox(state)) : Shapes.block();
+ } catch (NullPointerException e) {
+ return Shapes.block();
+ }
+ // Kaiiju end
}

@Override
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Path.java b/src/main/java/net/minecraft/world/level/pathfinder/Path.java
index 2a335f277bd0e4b8ad0f60d8226eb8aaa80a871f..b2c3c459fae7d0cb5ef0fcbc2ff0e61c7b952087 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/Path.java
Expand Down Expand Up @@ -1171,7 +1189,7 @@ 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..64b4cc587e2380a3d83916ea6ca1e1458458ceb0 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 {
Expand Down Expand Up @@ -1207,11 +1225,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
Expand All @@ -1227,12 +1241,16 @@ 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);
+ }
+
+ return new dev.kaiijumc.kaiiju.path.AsyncPath(Lists.newArrayList(), positions, () -> {
+ try {
+ return this.processPath(nodeEvaluator, node, map, followRange, distance, rangeMultiplier);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ nodeEvaluator.done();
+ dev.kaiijumc.kaiiju.path.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
Expand All @@ -1245,18 +1263,17 @@ 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();
Expand All @@ -1271,6 +1288,14 @@ index d23481453717f715124156b5d83f6448f720d049..d4cc2a5f99f8445be9a63c279e28032f

for(int l = 0; l < k; ++l) {
Node node2 = this.neighbors[l];
@@ -123,6 +163,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
Expand Down

0 comments on commit dc49313

Please sign in to comment.