Pathfinding fixes
Fix shulker boxes breaking pathfinding Fix petal memory leak Remove the thread queue
This commit is contained in:
@@ -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,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;
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user