From dc4931389bcaa8d8d1b7076ca1ed7f566ca0cafc Mon Sep 17 00:00:00 2001 From: "Sofiane H. Djerbi" <46628754+kugge@users.noreply.github.com> Date: Sat, 5 Aug 2023 02:59:48 +0200 Subject: [PATCH] Pathfinding fixes Fix shulker boxes breaking pathfinding Fix petal memory leak Remove the thread queue --- .../server/0043-Async-path-processing.patch | 91 ++++++++++++------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/patches/server/0043-Async-path-processing.patch b/patches/server/0043-Async-path-processing.patch index 9591dc7..3f56a73 100644 --- a/patches/server/0043-Async-path-processing.patch +++ b/patches/server/0043-Async-path-processing.patch @@ -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 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> threadLocalNodeEvaluators = new ConcurrentHashMap<>(); + private static final Map nodeEvaluatorToGenerator = new ConcurrentHashMap<>(); + -+ private static @NotNull Queue getDequeForGenerator(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) { ++ private static @NotNull Queue 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> positions, float followRange, int distance, float rangeMultiplier) { -+ private Path findPath(NodeEvaluator nodeEvaluator, ProfilerFiller profiler, Node startNode, List> positions, float followRange, int distance, float rangeMultiplier) { + private Path findPath(ProfilerFiller profiler, Node startNode, List> 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> 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> 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 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