-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy path0006-Async-Pathfinding.patch
1449 lines (1403 loc) · 70.7 KB
/
0006-Async-Pathfinding.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: zhangyuheng <[email protected]>
Date: Thu, 12 Dec 2024 21:29:00 +0800
Subject: [PATCH] Async Pathfinding
diff --git a/src/main/java/cn/lunadeer/async/path/AsyncPath.java b/src/main/java/cn/lunadeer/async/path/AsyncPath.java
new file mode 100644
index 0000000000000000000000000000000000000000..730e12f8644ab2daa8c92df7e6241737ea161b90
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/AsyncPath.java
@@ -0,0 +1,295 @@
+package cn.lunadeer.async.path;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.pathfinder.Node;
+import net.minecraft.world.level.pathfinder.Path;
+import net.minecraft.world.phys.Vec3;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * i'll be using this to represent a path that not be processed yet!
+ */
+public class AsyncPath extends Path {
+
+ /**
+ * marks whether this async path has been processed
+ */
+ private volatile PathProcessState processState = PathProcessState.WAITING;
+
+ /**
+ * runnables waiting for this to be processed
+ */
+ private final List<Runnable> postProcessing = new ArrayList<>(0);
+
+ /**
+ * a list of positions that this path could path towards
+ */
+ private final Set<BlockPos> positions;
+
+ /**
+ * the supplier of the real processed path
+ */
+ private final Supplier<Path> pathSupplier;
+
+ /*
+ * Processed values
+ */
+
+ /**
+ * this is a reference to the nodes list in the parent `Path` object
+ */
+ private final List<Node> nodes;
+ /**
+ * the block we're trying to path to
+ * <p>
+ * while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block
+ */
+ private @Nullable BlockPos target;
+ /**
+ * how far we are to the target
+ * <p>
+ * while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0
+ */
+ private float distToTarget = 0;
+ /**
+ * whether we can reach the target
+ * <p>
+ * while processing, we can always theoretically reach the target so default is true
+ */
+ private boolean canReach = true;
+
+ public AsyncPath(@NotNull List<Node> emptyNodeList, @NotNull Set<BlockPos> positions, @NotNull Supplier<Path> pathSupplier) {
+ //noinspection ConstantConditions
+ super(emptyNodeList, null, false);
+
+ this.nodes = emptyNodeList;
+ this.positions = positions;
+ this.pathSupplier = pathSupplier;
+
+ AsyncPathProcessor.queue(this);
+ }
+
+ @Override
+ public boolean isProcessed() {
+ return this.processState == PathProcessState.COMPLETED;
+ }
+
+ /**
+ * returns the future representing the processing state of this path
+ */
+ public synchronized void postProcessing(@NotNull Runnable runnable) {
+ if (isProcessed()) {
+ runnable.run();
+ } else {
+ this.postProcessing.add(runnable);
+ }
+ }
+
+ /**
+ * an easy way to check if this processing path is the same as an attempted new path
+ *
+ * @param positions - the positions to compare against
+ * @return true if we are processing the same positions
+ */
+ public boolean hasSameProcessingPositions(final Set<BlockPos> positions) {
+ if (this.positions.size() != positions.size()) {
+ return false;
+ }
+
+ return this.positions.containsAll(positions);
+ }
+
+ /**
+ * starts processing this path
+ */
+ public synchronized void process() {
+ if (this.processState == PathProcessState.COMPLETED ||
+ this.processState == PathProcessState.PROCESSING) {
+ return;
+ }
+
+ processState = PathProcessState.PROCESSING;
+
+ final Path bestPath = this.pathSupplier.get();
+
+ this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path
+ this.target = bestPath.getTarget();
+ this.distToTarget = bestPath.getDistToTarget();
+ this.canReach = bestPath.canReach();
+
+ processState = PathProcessState.COMPLETED;
+
+ for (Runnable runnable : this.postProcessing) {
+ runnable.run();
+ } // Run tasks after processing
+ }
+
+ /**
+ * if this path is accessed while it hasn't processed, just process it in-place
+ */
+ private void checkProcessed() {
+ if (this.processState == PathProcessState.WAITING ||
+ this.processState == PathProcessState.PROCESSING) { // Block if we are on processing
+ this.process();
+ }
+ }
+
+ /*
+ * overrides we need for final fields that we cannot modify after processing
+ */
+
+ @Override
+ public @NotNull BlockPos getTarget() {
+ this.checkProcessed();
+
+ return this.target;
+ }
+
+ @Override
+ public float getDistToTarget() {
+ this.checkProcessed();
+
+ return this.distToTarget;
+ }
+
+ @Override
+ public boolean canReach() {
+ this.checkProcessed();
+
+ return this.canReach;
+ }
+
+ /*
+ * overrides to ensure we're processed first
+ */
+
+ @Override
+ public boolean isDone() {
+ return this.processState == PathProcessState.COMPLETED && super.isDone();
+ }
+
+ @Override
+ public void advance() {
+ this.checkProcessed();
+
+ super.advance();
+ }
+
+ @Override
+ public boolean notStarted() {
+ this.checkProcessed();
+
+ return super.notStarted();
+ }
+
+ @Nullable
+ @Override
+ public Node getEndNode() {
+ this.checkProcessed();
+
+ return super.getEndNode();
+ }
+
+ @Override
+ public Node getNode(int index) {
+ this.checkProcessed();
+
+ return super.getNode(index);
+ }
+
+ @Override
+ public void truncateNodes(int length) {
+ this.checkProcessed();
+
+ super.truncateNodes(length);
+ }
+
+ @Override
+ public void replaceNode(int index, Node node) {
+ this.checkProcessed();
+
+ super.replaceNode(index, node);
+ }
+
+ @Override
+ public int getNodeCount() {
+ this.checkProcessed();
+
+ return super.getNodeCount();
+ }
+
+ @Override
+ public int getNextNodeIndex() {
+ this.checkProcessed();
+
+ return super.getNextNodeIndex();
+ }
+
+ @Override
+ public void setNextNodeIndex(int nodeIndex) {
+ this.checkProcessed();
+
+ super.setNextNodeIndex(nodeIndex);
+ }
+
+ @Override
+ public Vec3 getEntityPosAtNode(Entity entity, int index) {
+ this.checkProcessed();
+
+ return super.getEntityPosAtNode(entity, index);
+ }
+
+ @Override
+ public BlockPos getNodePos(int index) {
+ this.checkProcessed();
+
+ return super.getNodePos(index);
+ }
+
+ @Override
+ public Vec3 getNextEntityPos(Entity entity) {
+ this.checkProcessed();
+
+ return super.getNextEntityPos(entity);
+ }
+
+ @Override
+ public BlockPos getNextNodePos() {
+ this.checkProcessed();
+
+ return super.getNextNodePos();
+ }
+
+ @Override
+ public Node getNextNode() {
+ this.checkProcessed();
+
+ return super.getNextNode();
+ }
+
+ @Nullable
+ @Override
+ public Node getPreviousNode() {
+ this.checkProcessed();
+
+ return super.getPreviousNode();
+ }
+
+ @Override
+ public boolean hasNext() {
+ this.checkProcessed();
+
+ return super.hasNext();
+ }
+
+ public PathProcessState getProcessState() {
+ return processState;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/AsyncPathProcessor.java b/src/main/java/cn/lunadeer/async/path/AsyncPathProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d965ddf1ab51f86d016c8368ffb51985afb420
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/AsyncPathProcessor.java
@@ -0,0 +1,53 @@
+package cn.lunadeer.async.path;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import io.papermc.paper.configuration.GlobalConfiguration;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.pathfinder.Path;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.*;
+import java.util.function.Consumer;
+
+/**
+ * used to handle the scheduling of async path processing
+ */
+public class AsyncPathProcessor {
+
+ private static final Executor pathProcessingExecutor = new ThreadPoolExecutor(
+ 1,
+ GlobalConfiguration.get().asyncPathfinding.asyncPathfindingMaxThreads,
+ GlobalConfiguration.get().asyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(),
+ new ThreadFactoryBuilder()
+ .setNameFormat("DeerFolia Async Pathfinding Thread - %d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
+ .build()
+ );
+
+ protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
+ return CompletableFuture.runAsync(path::process, pathProcessingExecutor);
+ }
+
+ /**
+ * takes a possibly unprocessed path, and waits until it is completed
+ * the consumer will be immediately invoked if the path is already processed
+ * the consumer will always be called on the main thread
+ *
+ * @param path a path to wait on
+ * @param afterProcessing a consumer to be called
+ */
+ public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) {
+ if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) {
+ asyncPath.postProcessing(() ->
+ afterProcessing.accept(path)
+ );
+ } else {
+ afterProcessing.accept(path);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/NodeEvaluatorCache.java b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae46e584435d3d9c2ca65ddf4f8af9c06c30ae2d
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorCache.java
@@ -0,0 +1,45 @@
+package cn.lunadeer.async.path;
+
+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
+import net.minecraft.world.level.pathfinder.NodeEvaluator;
+
+import org.apache.commons.lang.Validate;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class NodeEvaluatorCache {
+ private static final Map<NodeEvaluatorFeatures, MultiThreadedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
+ private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
+
+ private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
+ return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new MultiThreadedQueue<>());
+ }
+
+ public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator);
+ NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll();
+
+ if (nodeEvaluator == null) {
+ nodeEvaluator = generator.generate(nodeEvaluatorFeatures);
+ }
+
+ nodeEvaluatorToGenerator.put(nodeEvaluator, generator);
+
+ return nodeEvaluator;
+ }
+
+ public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
+ final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
+ Validate.notNull(generator, "NodeEvaluator already returned");
+
+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
+ getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator);
+ }
+
+ public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
+ nodeEvaluatorToGenerator.remove(nodeEvaluator);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/NodeEvaluatorFeatures.java b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorFeatures.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa89a9f7bce49080d64dd1ff228638170b9f0f25
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorFeatures.java
@@ -0,0 +1,23 @@
+package cn.lunadeer.async.path;
+
+import net.minecraft.world.level.pathfinder.NodeEvaluator;
+import net.minecraft.world.level.pathfinder.SwimNodeEvaluator;
+
+public record NodeEvaluatorFeatures(
+ NodeEvaluatorType type,
+ boolean canPassDoors,
+ boolean canFloat,
+ boolean canWalkOverFences,
+ boolean canOpenDoors,
+ boolean allowBreaching
+) {
+ public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
+ NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator);
+ boolean canPassDoors = nodeEvaluator.canPassDoors();
+ boolean canFloat = nodeEvaluator.canFloat();
+ boolean canWalkOverFences = nodeEvaluator.canWalkOverFences();
+ boolean canOpenDoors = nodeEvaluator.canOpenDoors();
+ boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching;
+ return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/NodeEvaluatorGenerator.java b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..5906592b5bb31fe753850bedfd955fbe521988cc
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorGenerator.java
@@ -0,0 +1,11 @@
+package cn.lunadeer.async.path;
+
+import net.minecraft.world.level.pathfinder.NodeEvaluator;
+import org.jetbrains.annotations.NotNull;
+
+public interface NodeEvaluatorGenerator {
+
+ @NotNull
+ NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures);
+
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/NodeEvaluatorType.java b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorType.java
new file mode 100644
index 0000000000000000000000000000000000000000..d78f3b67d3bf4f8a6f4dc9eec9c542c18b19ed10
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/NodeEvaluatorType.java
@@ -0,0 +1,17 @@
+package cn.lunadeer.async.path;
+
+import net.minecraft.world.level.pathfinder.*;
+
+public enum NodeEvaluatorType {
+ WALK,
+ SWIM,
+ AMPHIBIOUS,
+ FLY;
+
+ public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
+ if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM;
+ if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY;
+ if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS;
+ return WALK;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/lunadeer/async/path/PathProcessState.java b/src/main/java/cn/lunadeer/async/path/PathProcessState.java
new file mode 100644
index 0000000000000000000000000000000000000000..0773bb8c129fa7f271d4df5cf5f3bc4926ae71f7
--- /dev/null
+++ b/src/main/java/cn/lunadeer/async/path/PathProcessState.java
@@ -0,0 +1,7 @@
+package cn.lunadeer.async.path;
+
+public enum PathProcessState {
+ WAITING,
+ PROCESSING,
+ COMPLETED,
+}
\ No newline at end of file
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
index 44cd9a9467441035a26bba3f48c4ca7c21b181a7..a827c87545cb6bf865dad48aa148e21418fcecf0 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -378,4 +378,24 @@ public class GlobalConfiguration extends ConfigurationPart {
public int activationDistanceMod = 8;
}
// DeerFolia end - puffish DAB
+
+ // DeerFolia start - async pathfinding
+ public AsyncPathfinding asyncPathfinding;
+ public class AsyncPathfinding extends ConfigurationPart {
+ public boolean enabled = true;
+ public int asyncPathfindingMaxThreads = 1;
+ public int asyncPathfindingKeepalive = 60;
+
+ @PostProcess
+ public void postProcess() {
+ if (this.enabled) {
+ if (asyncPathfindingMaxThreads < 0) {
+ asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
+ } else if (asyncPathfindingMaxThreads == 0) {
+ asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
+ }
+ }
+ }
+ }
+ // DeerFolia end - async pathfinding
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 0d177e828c2b338ce93c58aaef04df326e1eb0b2..93f68172816c7a31c9b1920cf1aebf2b49ad72d6 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -94,6 +94,39 @@ public class AcquirePoi {
}
}
// Paper end - optimise POI access
+
+ // DeerFolia start - petal - Async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ // await on path async
+ Path possiblePath = findPathToPois(entity, set);
+
+ // wait on the path to be processed
+ cn.lunadeer.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ // read canReach check
+ if (path == null || !path.canReach()) {
+ for (Pair<Holder<PoiType>, BlockPos> pair : set) {
+ long2ObjectMap.computeIfAbsent(
+ pair.getSecond().asLong(),
+ m -> new JitteredLinearRetry(entity.level().random, time)
+ );
+ }
+ return;
+ }
+ BlockPos blockPos = path.getTarget();
+ poiManager.getTypeWithoutLoad(blockPos).ifPresent(poiType -> {
+ poiManager.take(poiPredicate,
+ (holder, blockPos2) -> blockPos2.equals(blockPos),
+ blockPos,
+ 1
+ );
+ queryResult.set(GlobalPos.of(world.dimension(), blockPos));
+ entityStatus.ifPresent(status -> world.broadcastEntityEvent(entity, status));
+ long2ObjectMap.clear();
+ DebugPackets.sendPoiTicketCountPacket(world, blockPos);
+ });
+ });
+ } else {
+ // DeerFolia end
Path path = findPathToPois(entity, set);
if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget();
@@ -111,7 +144,7 @@ public class AcquirePoi {
);
}
}
-
+ } // DeerFolia - Async path processing
return true;
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index 2a7a26ca447cc78f24e61a2bf557411c31eb16b2..80b23a9b5c78a4ed61d137b9fb954a099ecb4468 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/src/main/java/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; // DeerFolia - 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 = entity.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean bl = this.reachedTarget(entity, walkTarget);
- if (!bl && this.tryComputePath(entity, walkTarget, world.getGameTime())) {
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled && !bl && this.tryComputePath(entity, walkTarget, world.getGameTime())) { // DeerFolia - petal - async path processing means we can't know if the path is reachable here
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
return true;
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled && !bl) { return true; // DeerFolia - async pathfinding
} else {
brain.eraseMemory(MemoryModuleType.WALK_TARGET);
if (bl) {
@@ -69,6 +71,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected boolean canStillUse(ServerLevel world, Mob entity, long time) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled && !this.finishedProcessing) return true; // DeerFolia - petal - wait for processing
if (this.path != null && this.lastTargetPos != null) {
Optional<WalkTarget> optional = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET);
boolean bl = optional.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false);
@@ -95,12 +98,68 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected void start(ServerLevel serverLevel, Mob mob, long l) {
+ // DeerFolia start - petal - start processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ Brain<?> brain = mob.getBrain();
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
+
+ this.finishedProcessing = false;
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ this.path = this.computePath(mob, walkTarget);
+ return;
+ }
+ // DeerFolia end
mob.getBrain().setMemory(MemoryModuleType.PATH, this.path);
mob.getNavigation().moveTo(this.path, (double)this.speedModifier);
}
@Override
protected void tick(ServerLevel serverLevel, Mob mob, long l) {
+ // DeerFolia start - petal - Async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ if (this.path != null && !this.path.isProcessed()) return; // wait for processing
+
+ if (!this.finishedProcessing) {
+ this.finishedProcessing = true;
+
+ Brain<?> brain = mob.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, l);
+ }
+
+ 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) mob, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F);
+ if (vec3 != null) {
+ // try recalculating the path using a random position
+ this.path = mob.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
+ this.finishedProcessing = false;
+ return;
+ }
+ }
+
+ mob.getBrain().setMemory(MemoryModuleType.PATH, this.path);
+ mob.getNavigation().moveTo(this.path, this.speedModifier);
+ }
+
+ Path path = mob.getNavigation().getPath();
+ Brain<?> brain = mob.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(serverLevel, mob, l);
+ }
+ }
+ } else {
+ // DeerFolia end
Path path = mob.getNavigation().getPath();
Brain<?> brain = mob.getBrain();
if (this.path != path) {
@@ -116,7 +175,23 @@ public class MoveToTargetSink extends Behavior<Mob> {
this.start(serverLevel, mob, l);
}
}
+ } // DeerFolia - async path processing
+ }
+
+ // DeerFolia 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; // DeerFolia - 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);
}
+ // DeerFolia end
private boolean tryComputePath(Mob entity, WalkTarget walkTarget, long time) {
BlockPos blockPos = walkTarget.getTarget().currentBlockPosition();
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
index 6802e0c4d331c7125114dd86409f6a110465ab82..eebfb465e26a8f3da2cb0bfb94d805d20052dc2a 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
@@ -60,6 +60,26 @@ public class SetClosestHomeAsWalkTarget {
poiType -> poiType.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY
)
.collect(Collectors.toSet());
+ // DeerFolia start - petal - Async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ // await on path async
+ Path possiblePath = AcquirePoi.findPathToPois(entity, set);
+
+ // wait on the path to be processed
+ cn.lunadeer.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ if (path == null || !path.canReach() || mutableInt.getValue() < 5) { // read canReach check
+ long2LongMap.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
+ return;
+ }
+ BlockPos blockPos = path.getTarget();
+ Optional<Holder<PoiType>> optional2 = poiManager.getTypeWithoutLoad(blockPos);
+ if (optional2.isPresent()) {
+ walkTarget.set(new WalkTarget(blockPos, speed, 1));
+ DebugPackets.sendPoiTicketCountPacket(world, blockPos);
+ }
+ });
+ } else {
+ // DeerFolia end
Path path = AcquirePoi.findPathToPois(entity, set);
if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget();
@@ -71,6 +91,7 @@ public class SetClosestHomeAsWalkTarget {
} else if (mutableInt.getValue() < 5) {
long2LongMap.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
}
+ } // DeerFolia - async path processing
return true;
} else {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
index 2846790fcd00788cf0284c348161ee1aee415f13..df80fcce17aa8f8bcab0cd84ebc18e6e78440e1d 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
@@ -56,7 +56,7 @@ public abstract class DoorInteractGoal extends Goal {
} else {
GroundPathNavigation groundPathNavigation = (GroundPathNavigation)this.mob.getNavigation();
Path path = groundPathNavigation.getPath();
- if (path != null && !path.isDone()) {
+ if (path != null && path.isProcessed() && !path.isDone()) { // DeerFolia - 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/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
index 29b852c3262c9cd0d2c77a93c01a386a2c184742..c8d3442de4bb0ba1ff2bf8992860d884d28700b6 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation {
super(mob, world);
}
+ // DeerFolia start - petal - async path processing
+ private static final cn.lunadeer.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (cn.lunadeer.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;
+ };
+ // DeerFolia end
+
@Override
protected PathFinder createPathFinder(int range) {
this.nodeEvaluator = new AmphibiousNodeEvaluator(false);
+ // DeerFolia start - petal - async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator);
+ }
+ // DeerFolia end
return new PathFinder(this.nodeEvaluator, range);
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
index 2bd66da93227d4e4fc2ec4df47ae94b17f4d39d3..140a3b4cadfd8aa9f51c5ac9726abd7d91aec1e1 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -16,9 +16,25 @@ public class FlyingPathNavigation extends PathNavigation {
super(entity, world);
}
+ // DeerFolia start - petal - async path processing
+ private static final cn.lunadeer.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (cn.lunadeer.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;
+ };
+ // DeerFolia end
+
@Override
protected PathFinder createPathFinder(int range) {
this.nodeEvaluator = new FlyNodeEvaluator();
+ // DeerFolia start - petal - async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator);
+ }
+ // DeerFolia end
return new PathFinder(this.nodeEvaluator, range);
}
@@ -48,6 +64,7 @@ public class FlyingPathNavigation extends PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // DeerFolia - petal - async path processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
index 45b68a139956f2d59c8b9658692d01f1f79d33cc..2377691a99dd4795cc1e862ea4a4dad9e3c0f5c3 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
@@ -24,9 +24,25 @@ public class GroundPathNavigation extends PathNavigation {
super(entity, world);
}
+ // DeerFolia start - petal - async path processing
+ protected static final cn.lunadeer.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (cn.lunadeer.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;
+ };
+ // DeerFolia end
+
@Override
protected PathFinder createPathFinder(int range) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // DeerFolia start - petal - async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator);
+ }
+ // DeerFolia end
return new PathFinder(this.nodeEvaluator, range);
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 71885b8b3f491fbe644efef02aebdd8b9f03a10b..20f120ed8500ae8d3c1a40443f08234eac6f1898 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -168,6 +168,10 @@ public abstract class PathNavigation {
return null;
} else if (!this.canUpdatePath()) {
return null;
+ // DeerFolia start - petal - catch early if it's still processing these positions let it keep processing
+ } else if (this.path instanceof cn.lunadeer.async.path.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(positions)) {
+ return this.path;
+ // DeerFolia end
} else if (this.path != null && !this.path.isDone() && positions.contains(this.targetPos)) {
return this.path;
} else {
@@ -195,11 +199,29 @@ public abstract class PathNavigation {
PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i));
Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, positions, followRange, distance, this.maxVisitedNodesMultiplier);
profilerFiller.pop();
+ // DeerFolia start - petal - async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ // assign early a target position. most calls will only have 1 position
+ if (!positions.isEmpty()) this.targetPos = positions.iterator().next();
+
+ cn.lunadeer.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 = distance;
+ this.resetStuckTimeout();
+ }
+ });
+ } else {
+ // DeerFolia end
if (path != null && path.getTarget() != null) {
this.targetPos = path.getTarget();
this.reachRange = distance;
this.resetStuckTimeout();
}
+ } // DeerFolia - async path processing
return path;
}
@@ -250,8 +272,8 @@ public abstract class PathNavigation {
if (this.isDone()) {
return false;
} else {
- this.trimPath();
- if (this.path.getNodeCount() <= 0) {
+ if (path.isProcessed()) this.trimPath(); // DeerFolia - petal - only trim if processed
+ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // DeerFolia - petal - only check node count if processed
return false;
} else {
this.speedModifier = speed;
@@ -274,6 +296,7 @@ public abstract class PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // DeerFolia - petal - skip pathfinding if we're still processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
@@ -300,6 +323,7 @@ public abstract class PathNavigation {
}
protected void followThePath() {
+ if (!this.path.isProcessed()) return; // DeerFolia - petal - skip if not processed
Vec3 vec3 = this.getTempMobPos();
this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F;
Vec3i vec3i = this.path.getNextNodePos();
@@ -456,7 +480,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) { // DeerFolia - petal - Skip if not processed
Node node = this.path.getEndNode();
Vec3 vec3 = new Vec3(((double)node.x + this.mob.getX()) / 2.0, ((double)node.y + this.mob.getY()) / 2.0, ((double)node.z + this.mob.getZ()) / 2.0);
return pos.closerToCenterThan(vec3, (double)(this.path.getNodeCount() - this.path.getNextNodeIndex()));
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
index 943c9944ae17fa7cd72e437cce61beaf3fc9505e..32df610ac69d5dcf4216feffdeef20968a9c6e50 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation {
super(entity, world);
}
+ // DeerFolia start - petal - async path processing
+ private static final cn.lunadeer.async.path.NodeEvaluatorGenerator nodeEvaluatorGenerator = (cn.lunadeer.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;
+ };
+ // DeerFolia end
+
@Override
protected PathFinder createPathFinder(int range) {
this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN;
this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching);
this.nodeEvaluator.setCanPassDoors(false);
+ // DeerFolia start - async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator);
+ }
+ // DeerFolia end
return new PathFinder(this.nodeEvaluator, range);
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 92731b6b593289e9f583c9b705b219e81fcd8e73..93e2844f9adbbeb0852ba7e26e555f5f2e4e490a 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -57,6 +57,25 @@ 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(), 48, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes);
+ // DeerFolia start - await on async path processing
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().asyncPathfinding.enabled) {
+ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ cn.lunadeer.async.path.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ // read canReach check
+ if ((path == null || !path.canReach()) && this.triedCount < 5) {
+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
+ return;
+ }
+ if (path == null) return;
+
+ BlockPos blockPos = path.getTarget();
+ Optional<Holder<PoiType>> optional = poiManager.getTypeWithoutLoad(blockPos);
+ if (optional.isPresent()) {
+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, blockPos);
+ }