This repository has been archived by the owner on Sep 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 47
/
0074-lithium-ai.nearby_entity_tracking-and-ai.nearby_enti.patch
1060 lines (1028 loc) · 49.5 KB
/
0074-lithium-ai.nearby_entity_tracking-and-ai.nearby_enti.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: 2No2Name <2No2Name@web.de>
Date: Thu, 13 Jan 2022 00:47:37 -0500
Subject: [PATCH] lithium: ai.nearby_entity_tracking and
ai.nearby_entity_tracking.goals
Original code by CaffeineMC, licensed under GNU Lesser General Public License v3.0
You can find the original code on https://github.com/CaffeineMC/lithium-fabric (Yarn mappings)
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..af1efa5f7abfcf9440818f7b13e97bb65a9b2323
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java
@@ -0,0 +1,55 @@
+package me.jellysquid.mods.lithium.common.entity.tracker;
+
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.List;
+import net.minecraft.world.Container;
+import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.level.entity.EntityAccess;
+
+/**
+ * Helps to track in which entity sections entities of a certain type moved, appeared or disappeared by providing int
+ * masks for all Entity classes.
+ * Helps to track the entities within a world and provide notifications to listeners when a tracked entity enters or leaves a
+ * watched area. This removes the necessity to constantly poll the world for nearby entities each tick and generally
+ * provides a sizable boost to performance of hoppers.
+ */
+public abstract class EntityTrackerEngine {
+ public static final List<Class<?>> MOVEMENT_NOTIFYING_ENTITY_CLASSES;
+ public static volatile Reference2IntOpenHashMap<Class<? extends EntityAccess>> CLASS_2_NOTIFY_MASK;
+ public static final int NUM_MOVEMENT_NOTIFYING_CLASSES;
+
+ static {
+ MOVEMENT_NOTIFYING_ENTITY_CLASSES = List.of(ItemEntity.class, Container.class);
+
+ CLASS_2_NOTIFY_MASK = new Reference2IntOpenHashMap<>();
+ CLASS_2_NOTIFY_MASK.defaultReturnValue(-1);
+ NUM_MOVEMENT_NOTIFYING_CLASSES = MOVEMENT_NOTIFYING_ENTITY_CLASSES.size();
+ }
+
+ public static int getNotificationMask(Class<? extends EntityAccess> entityClass) {
+ int notificationMask = CLASS_2_NOTIFY_MASK.getInt(entityClass);
+ if (notificationMask == -1) {
+ notificationMask = calculateNotificationMask(entityClass);
+ }
+ return notificationMask;
+ }
+
+ private static int calculateNotificationMask(Class<? extends EntityAccess> entityClass) {
+ int mask = 0;
+ for (int i = 0; i < MOVEMENT_NOTIFYING_ENTITY_CLASSES.size(); i++) {
+ Class<?> superclass = MOVEMENT_NOTIFYING_ENTITY_CLASSES.get(i);
+ if (superclass.isAssignableFrom(entityClass)) {
+ mask |= 1 << i;
+ }
+ }
+
+ //progress can be lost here, but it can only cost performance
+ //copy on write followed by publication in volatile field guarantees visibility of the final state
+ Reference2IntOpenHashMap<Class<? extends EntityAccess>> copy = CLASS_2_NOTIFY_MASK.clone();
+ copy.put(entityClass, mask);
+ CLASS_2_NOTIFY_MASK = copy;
+
+ return mask;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dd34c4028fba33f2449cde5f9228283a3cb69dc
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java
@@ -0,0 +1,94 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import me.jellysquid.mods.lithium.common.util.tuples.Range6Int;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.SectionPos;
+import net.minecraft.util.ClassInstanceMultiMap;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.entity.EntityAccess;
+import net.minecraft.world.level.entity.EntitySection;
+import net.minecraft.world.level.entity.EntitySectionStorage;
+import net.minecraft.world.level.levelgen.structure.BoundingBox;
+
+/**
+ * The main interface used to receive events from the
+ * {@link me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine} of a world.
+ */
+public interface NearbyEntityListener {
+ Range6Int EMPTY_RANGE = new Range6Int(0, 0, 0, -1, -1, -1);
+ /**
+ * Calls the callbacks for the chunk coordinates that this listener is leaving and entering
+ */
+ default void forEachChunkInRangeChange(EntitySectionStorage<? extends EntityAccess> entityCache, SectionPos prevCenterPos, SectionPos newCenterPos) {
+ Range6Int chunkRange = this.getChunkRange();
+ if (chunkRange == EMPTY_RANGE) {
+ return;
+ }
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
+
+ BoundingBox after = newCenterPos == null ? null : new BoundingBox(newCenterPos.getX() - chunkRange.negativeX(), newCenterPos.getY() - chunkRange.negativeY(), newCenterPos.getZ() - chunkRange.negativeZ(), newCenterPos.getX() + chunkRange.positiveX(), newCenterPos.getY() + chunkRange.positiveY(), newCenterPos.getZ() + chunkRange.positiveZ());
+ BoundingBox before = prevCenterPos == null ? null : new BoundingBox(prevCenterPos.getX() - chunkRange.negativeX(), prevCenterPos.getY() - chunkRange.negativeY(), prevCenterPos.getZ() - chunkRange.negativeZ(), prevCenterPos.getX() + chunkRange.positiveX(), prevCenterPos.getY() + chunkRange.positiveY(), prevCenterPos.getZ() + chunkRange.positiveZ());
+ if (before != null) {
+ for (int x = before.minX(); x <= before.maxX(); x++) {
+ for (int y = before.minY(); y <= before.maxY(); y++) {
+ for (int z = before.minZ(); z <= before.maxZ(); z++) {
+ if (after == null || !after.isInside(pos.set(x, y, z))) {
+ long sectionPos = SectionPos.asLong(x, y, z);
+ EntitySection<? extends EntityAccess> trackingSection = entityCache.getOrCreateSection(sectionPos);
+ trackingSection.removeListener(entityCache, this);
+ if (trackingSection.isEmpty()) {
+ entityCache.remove(sectionPos);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (after != null) {
+ for (int x = after.minX(); x <= after.maxX(); x++) {
+ for (int y = after.minY(); y <= after.maxY(); y++) {
+ for (int z = after.minZ(); z <= after.maxZ(); z++) {
+ if (before == null || !before.isInside(pos.set(x, y, z))) {
+ entityCache.getOrCreateSection(SectionPos.asLong(x, y, z)).addListener(this);
+ }
+ }
+ }
+ }
+ }
+ }
+ Range6Int getChunkRange();
+
+ /**
+ * Called by the entity tracker when an entity enters the range of this listener.
+ */
+ void onEntityEnteredRange(Entity entity);
+
+ /**
+ * Called by the entity tracker when an entity leaves the range of this listener or is removed from the world.
+ */
+ void onEntityLeftRange(Entity entity);
+
+ default Class<? extends Entity> getEntityClass() {
+ return Entity.class;
+ }
+
+ /**
+ * Method to add all entities in the iteration order of the chunk section. This order is relevant and necessary
+ * to keep vanilla parity.
+ * @param <T> the type of the Entities in the collection
+ * @param entityTrackingSection the section the entities are in
+ * @param collection the collection of Entities that entered the range of this listener
+ */
+ default <T> void onSectionEnteredRange(Object entityTrackingSection, ClassInstanceMultiMap<T> collection) {
+ for (Entity entity : collection.find(this.getEntityClass())) {
+ this.onEntityEnteredRange(entity);
+ }
+ }
+
+ default <T> void onSectionLeftRange(Object entityTrackingSection, ClassInstanceMultiMap<T> collection) {
+ for (Entity entity : collection.find(this.getEntityClass())) {
+ this.onEntityLeftRange(entity);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d7a656e683987b80916abaf1dcb10f77b9a8a4a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java
@@ -0,0 +1,83 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import me.jellysquid.mods.lithium.common.util.tuples.Range6Int;
+import net.minecraft.world.entity.Entity;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows for multiple listeners on an entity to be grouped under one logical listener. No guarantees are made about the
+ * order of which each sub-listener will be notified.
+ */
+public class NearbyEntityListenerMulti implements NearbyEntityListener {
+ private final List<NearbyEntityListener> listeners = new ArrayList<>(4);
+ private Range6Int range = null;
+
+ public void addListener(NearbyEntityListener listener) {
+ if (this.range != null) {
+ throw new IllegalStateException("Cannot add sublisteners after listening range was set!");
+ }
+ this.listeners.add(listener);
+ }
+
+ public void removeListener(NearbyEntityListener listener) {
+ this.listeners.remove(listener);
+ }
+
+ @Override
+ public Range6Int getChunkRange() {
+ if (this.range != null) {
+ return this.range;
+ }
+ return this.calculateRange();
+ }
+ private Range6Int calculateRange() {
+ if (this.listeners.isEmpty()) {
+ return this.range = EMPTY_RANGE;
+ }
+ int positiveX = -1;
+ int positiveY = -1;
+ int positiveZ = -1;
+ int negativeX = 0;
+ int negativeY = 0;
+ int negativeZ = 0;
+
+ for (NearbyEntityListener listener : this.listeners) {
+ Range6Int chunkRange = listener.getChunkRange();
+ positiveX = Math.max(chunkRange.positiveX(), positiveX);
+ positiveY = Math.max(chunkRange.positiveY(), positiveY);
+ positiveZ = Math.max(chunkRange.positiveZ(), positiveZ);
+ negativeX = Math.max(chunkRange.negativeX(), negativeX);
+ negativeY = Math.max(chunkRange.negativeY(), negativeY);
+ negativeZ = Math.max(chunkRange.negativeZ(), negativeZ);
+
+ }
+ return this.range = new Range6Int(positiveX, positiveY, positiveZ, negativeX, negativeY, negativeZ);
+ }
+
+ @Override
+ public void onEntityEnteredRange(Entity entity) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityEnteredRange(entity);
+ }
+ }
+
+ @Override
+ public void onEntityLeftRange(Entity entity) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityLeftRange(entity);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sublisteners = new StringBuilder();
+ String comma = "";
+ for (NearbyEntityListener listener : this.listeners) {
+ sublisteners.append(comma).append(listener.toString());
+ comma = ","; //trick to drop the first comma
+ }
+
+ return super.toString() + " with sublisteners: [" + sublisteners + "]";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..08d0be81d25b8b35506a7c03942356d0f33c2d89
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java
@@ -0,0 +1,140 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
+import me.jellysquid.mods.lithium.common.util.tuples.Range6Int;
+import net.minecraft.core.SectionPos;
+import net.minecraft.core.Vec3i;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.ai.targeting.TargetingConditions;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.phys.AABB;
+import java.util.List;
+
+/**
+ * Maintains a collection of all entities within the range of this listener. This allows AI goals to quickly
+ * assess nearby entities which match the provided class.
+ */
+public class NearbyEntityTracker<T extends LivingEntity> implements NearbyEntityListener {
+ private final Class<T> clazz;
+ private final LivingEntity self;
+
+ private final Reference2LongOpenHashMap<T> nearbyEntities = new Reference2LongOpenHashMap<>(0);
+ private long counter;
+ private final Range6Int chunkBoxRadius;
+
+ public NearbyEntityTracker(Class<T> clazz, LivingEntity self, Vec3i boxRadius) {
+ this.clazz = clazz;
+ this.self = self;
+ this.chunkBoxRadius = new Range6Int(
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getX()),
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getY()),
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getZ()),
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getX()),
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getY()),
+ 1 + SectionPos.blockToSectionCoord(boxRadius.getZ())
+ );
+ }
+
+ @Override
+ public Class<? extends Entity> getEntityClass() {
+ return this.clazz;
+ }
+
+ @Override
+ public Range6Int getChunkRange() {
+ return this.chunkBoxRadius;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onEntityEnteredRange(Entity entity) {
+ if (!this.clazz.isInstance(entity)) {
+ return;
+ }
+
+ this.nearbyEntities.put((T) entity, this.counter++);
+ }
+
+ @Override
+ public void onEntityLeftRange(Entity entity) {
+ if (this.nearbyEntities.isEmpty() || !this.clazz.isInstance(entity)) {
+ return;
+ }
+
+ this.nearbyEntities.removeLong(entity);
+ }
+
+ /**
+ * Gets the closest T (extends LivingEntity) to the center of this tracker that also intersects with the given box and meets the
+ * requirements of the targetPredicate.
+ * The result may be different from vanilla if there are multiple closest entities.
+ *
+ * @param box the box the entities have to intersect
+ * @param targetPredicate predicate the entity has to meet
+ * @param x
+ * @param y
+ * @param z
+ * @return the closest Entity that meets all requirements (distance, box intersection, predicate, type T)
+ */
+ public T getClosestEntity(AABB box, TargetingConditions targetPredicate, double x, double y, double z) {
+ T nearest = null;
+ double nearestDistance = Double.POSITIVE_INFINITY;
+
+ for (T entity : this.nearbyEntities.keySet()) {
+ double distance;
+ if (
+ (box == null || box.intersects(entity.getBoundingBox())) &&
+ (distance = entity.distanceToSqr(x, y, z)) <= nearestDistance &&
+ targetPredicate.test(this.self, entity)
+ ) {
+ if (distance == nearestDistance) {
+ nearest = this.getFirst(nearest, entity);
+ } else {
+ nearest = entity;
+ }
+ nearestDistance = distance;
+ }
+ }
+
+ return nearest;
+ }
+
+ /**
+ * Gets the Entity that is processed first in vanilla.
+ * @param entity1 one Entity
+ * @param entity2 the other Entity
+ * @return the Entity that is first in vanilla
+ */
+ private T getFirst(T entity1, T entity2) {
+ if (this.getEntityClass() == Player.class) {
+ //Get first in player list
+ List<? extends Player> players = this.self.getCommandSenderWorld().players();
+ return players.indexOf((Player)entity1) < players.indexOf((Player)entity2) ? entity1 : entity2;
+ } else {
+ //Get first sorted by chunk section pos as long, then sorted by first added to the chunk section
+ //First added to this tracker and first added to the chunk section is equivalent here, because
+ //this tracker always tracks complete sections and the entities are added in order
+ long pos1 = SectionPos.asLong(entity1.blockPosition());
+ long pos2 = SectionPos.asLong(entity2.blockPosition());
+ if (pos1 < pos2) {
+ return entity1;
+ } else if (pos2 < pos1) {
+ return entity2;
+ } else {
+ if (this.nearbyEntities.getLong(entity1) < this.nearbyEntities.getLong(entity2)) {
+ return entity1;
+ } else {
+ return entity2;
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " for entity class: " + this.clazz.getName() + ", around entity: " + this.self.toString() + " with NBT: " + this.self.saveWithoutId(new CompoundTag());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/SectionedEntityMovementTracker.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/SectionedEntityMovementTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5a4bd43eb3d900a237f659f8ad4e1a3be05eb97
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/SectionedEntityMovementTracker.java
@@ -0,0 +1,143 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import it.unimi.dsi.fastutil.HashCommon;
+import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine;
+import me.jellysquid.mods.lithium.common.util.tuples.WorldSectionBox;
+import net.minecraft.core.SectionPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.entity.EntityAccess;
+import net.minecraft.world.level.entity.EntitySection;
+import net.minecraft.world.level.entity.EntitySectionStorage;
+import java.util.ArrayList;
+import net.minecraft.world.entity.Entity;
+
+public abstract class SectionedEntityMovementTracker<E extends EntityAccess, S> {
+ final WorldSectionBox trackedWorldSections;
+ final Class<S> clazz;
+ private final int trackedClass;
+ ArrayList<EntitySection<Entity>> sortedSections;
+ boolean[] sectionVisible;
+ private int timesRegistered;
+ private ArrayList<long[]> sectionChangeCounters;
+
+ private long maxChangeTime;
+
+ public SectionedEntityMovementTracker(WorldSectionBox interactionChunks, Class<S> clazz) {
+ this.clazz = clazz;
+ this.trackedWorldSections = interactionChunks;
+ this.trackedClass = EntityTrackerEngine.MOVEMENT_NOTIFYING_ENTITY_CLASSES.indexOf(clazz);
+ assert this.trackedClass != -1;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCommon.mix(this.trackedWorldSections.hashCode()) ^ HashCommon.mix(this.trackedClass) ^ this.getClass().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj.getClass() == this.getClass() &&
+ this.clazz == ((SectionedEntityMovementTracker<?, ?>) obj).clazz &&
+ this.trackedWorldSections.equals(((SectionedEntityMovementTracker<?, ?>) obj).trackedWorldSections);
+ }
+
+ /**
+ * Method to quickly check whether any relevant entities moved inside the relevant entity sections after
+ * the last interaction attempt.
+ *
+ * @param lastCheckedTime time of the last interaction attempt
+ * @return whether any relevant entity moved in the tracked area
+ */
+ public boolean isUnchangedSince(long lastCheckedTime) {
+ if (lastCheckedTime <= this.maxChangeTime) {
+ return false;
+ }
+ ArrayList<long[]> sectionChangeCounters = this.sectionChangeCounters;
+ int trackedClass = this.trackedClass;
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0, numCounters = sectionChangeCounters.size(); i < numCounters; i++) {
+ // >= instead of > is required here, as changes may occur in the same tick but after the last check
+ long sectionChangeTime = sectionChangeCounters.get(i)[trackedClass];
+ if (lastCheckedTime <= sectionChangeTime) {
+ this.setChanged(sectionChangeTime);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void register(ServerLevel world) {
+ assert world == this.trackedWorldSections.world();
+
+ if (this.timesRegistered == 0) {
+ //noinspection unchecked
+ EntitySectionStorage<Entity> cache = world.entityManager.getCache();
+
+ this.sectionChangeCounters = new ArrayList<>();
+ WorldSectionBox trackedSections = this.trackedWorldSections;
+ int size = trackedSections.numSections();
+ assert size > 0;
+ this.sortedSections = new ArrayList<>(size);
+ this.sectionVisible = new boolean[size];
+
+ //vanilla iteration order in SectionedEntityCache is xzy
+ //WorldSectionBox upper coordinates are exclusive
+ for (int x = trackedSections.chunkX1(); x < trackedSections.chunkX2(); x++) {
+ for (int z = trackedSections.chunkZ1(); z < trackedSections.chunkZ2(); z++) {
+ for (int y = trackedSections.chunkY1(); y < trackedSections.chunkY2(); y++) {
+ EntitySection<Entity> section = cache.getOrCreateSection(SectionPos.asLong(x, y, z));
+ this.sortedSections.add(section);
+ section.addListener(this);
+ }
+ }
+ }
+ this.setChanged(world.getGameTime());
+ }
+
+ this.timesRegistered++;
+ }
+
+ public void unRegister(ServerLevel world) {
+ assert world == this.trackedWorldSections.world();
+ if (--this.timesRegistered > 0) {
+ return;
+ }
+ assert this.timesRegistered == 0;
+ //noinspection unchecked
+ EntitySectionStorage<Entity> cache = world.entityManager.getCache();
+ cache.remove(this);
+
+ ArrayList<EntitySection<Entity>> sections = this.sortedSections;
+ for (int i = sections.size() - 1; i >= 0; i--) {
+ EntitySection<Entity> section = sections.get(i);
+ section.removeListener(cache, this);
+ }
+ this.setChanged(world.getGameTime());
+ }
+
+ /**
+ * Register an entity section to this listener, so this listener can look for changes in the section.
+ */
+ public void onSectionEnteredRange(EntitySection section) {
+ this.setChanged(this.trackedWorldSections.world().getGameTime());
+ //noinspection SuspiciousMethodCalls
+ this.sectionVisible[this.sortedSections.lastIndexOf(section)] = true;
+ this.sectionChangeCounters.add(section.getMovementTimestampArray());
+ }
+
+ public void onSectionLeftRange(EntitySection section) {
+ this.setChanged(this.trackedWorldSections.world().getGameTime());
+ //noinspection SuspiciousMethodCalls
+ this.sectionVisible[this.sortedSections.indexOf(section)] = false;
+ this.sectionChangeCounters.remove(section.getMovementTimestampArray());
+ }
+
+ /**
+ * Method that marks that new entities might have appeared or moved in the tracked chunk sections.
+ */
+ private void setChanged(long atTime) {
+ if (atTime > this.maxChangeTime) {
+ this.maxChangeTime = atTime;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/Range6Int.java b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/Range6Int.java
new file mode 100644
index 0000000000000000000000000000000000000000..673d4b02a4baf779efa64c1d7b4fc284ec0887f9
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/Range6Int.java
@@ -0,0 +1,4 @@
+package me.jellysquid.mods.lithium.common.util.tuples;
+
+public record Range6Int(int negativeX, int negativeY, int negativeZ, int positiveX, int positiveY, int positiveZ) {
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/WorldSectionBox.java b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/WorldSectionBox.java
new file mode 100644
index 0000000000000000000000000000000000000000..178fc249777c9997e9586ffe099e54c9b1e1322a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/WorldSectionBox.java
@@ -0,0 +1,24 @@
+package me.jellysquid.mods.lithium.common.util.tuples;
+
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.AABB;
+import net.minecraft.core.SectionPos;
+
+//Y values use coordinates, not indices (y=0 -> chunkY=0)
+//upper bounds are EXCLUSIVE
+public record WorldSectionBox(Level world, int chunkX1, int chunkY1, int chunkZ1, int chunkX2, int chunkY2,
+ int chunkZ2) {
+ public static WorldSectionBox entityAccessBox(Level world, AABB box) {
+ int minX = SectionPos.posToSectionCoord((double)box.minX - 2.0D);
+ int minY = SectionPos.posToSectionCoord((double)box.minY - 4.0D);
+ int minZ = SectionPos.posToSectionCoord((double)box.minZ - 2.0D);
+ int maxX = SectionPos.posToSectionCoord((double)box.maxX + 2.0D) + 1;
+ int maxY = SectionPos.posToSectionCoord((double)box.maxY) + 1;
+ int maxZ = SectionPos.posToSectionCoord((double)box.maxZ + 2.0D) + 1;
+ return new WorldSectionBox(world, minX, minY, minZ, maxX, maxY, maxZ);
+ }
+
+ public int numSections() {
+ return (this.chunkX2 - this.chunkX1) * (this.chunkY2 - this.chunkY1) * (this.chunkZ2 - this.chunkZ1);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 139884cead5a6d9d5b5990943e5a4ab4336ad22a..4279b170440faf28a061360d40ed6303d8cc1a97 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -153,9 +153,29 @@ import org.bukkit.event.entity.EntityPoseChangeEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.PluginManager;
// CraftBukkit end
+// JettPack start
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListener;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListenerMulti;
+// JettPack end
public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ private NearbyEntityListenerMulti nearbyTracker = null;
+
+ @Nullable
+ public NearbyEntityListenerMulti getListener() {
+ return this.nearbyTracker;
+ }
+
+ public void addListener(NearbyEntityListener listener) {
+ if (this.nearbyTracker == null) {
+ this.nearbyTracker = new NearbyEntityListenerMulti();
+ }
+ this.nearbyTracker.addListener(listener);
+ }
+ // JettPack end
+
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
public boolean preserveMotion = true; // Paper - keep initial motion on first setPositionRotation
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java
index 66e5c2716684f54e15e931e33d09463c0df0fda3..4f5a0d57eba877ee90f35e134496a71ebb127b0c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java
@@ -11,6 +11,12 @@ import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import net.minecraft.world.entity.ai.util.DefaultRandomPos;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3;
+// JettPack start
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker;
+import net.minecraft.world.entity.EntityDimensions;
+import net.minecraft.util.Mth;
+import net.minecraft.core.Vec3i;
+// JettPack end
public class AvoidEntityGoal<T extends LivingEntity> extends Goal {
protected final PathfinderMob mob;
@@ -26,6 +32,7 @@ public class AvoidEntityGoal<T extends LivingEntity> extends Goal {
protected final Predicate<LivingEntity> avoidPredicate;
protected final Predicate<LivingEntity> predicateOnAvoidEntity;
private final TargetingConditions avoidEntityTargeting;
+ private NearbyEntityTracker<T> nearbyTracker; // JettPack
public AvoidEntityGoal(PathfinderMob mob, Class<T> fleeFromType, float distance, double slowSpeed, double fastSpeed) {
this(mob, fleeFromType, (livingEntity) -> {
@@ -44,6 +51,13 @@ public class AvoidEntityGoal<T extends LivingEntity> extends Goal {
this.pathNav = mob.getNavigation();
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
this.avoidEntityTargeting = TargetingConditions.forCombat().range((double)distance).selector(inclusionSelector.and(extraInclusionSelector));
+ // JettPack start - lithium: ai.nearby_entity_tracking.goals
+ EntityDimensions dimensions = this.mob.getType().getDimensions();
+ double adjustedRange = dimensions.width * 0.5D + this.maxDist + 2D;
+ int horizontalRange = Mth.ceil(adjustedRange);
+ this.nearbyTracker = new NearbyEntityTracker<>(fleeFromType, mob, new Vec3i(horizontalRange, Mth.ceil(dimensions.height + 3 + 2), horizontalRange));
+ mob.addListener(this.nearbyTracker);
+ // JettPack end
}
public AvoidEntityGoal(PathfinderMob fleeingEntity, Class<T> classToFleeFrom, float fleeDistance, double fleeSlowSpeed, double fleeFastSpeed, Predicate<LivingEntity> inclusionSelector) {
@@ -54,9 +68,7 @@ public class AvoidEntityGoal<T extends LivingEntity> extends Goal {
@Override
public boolean canUse() {
- this.toAvoid = this.mob.level.getNearestEntity(this.mob.level.getEntitiesOfClass(this.avoidClass, this.mob.getBoundingBox().inflate((double)this.maxDist, 3.0D, (double)this.maxDist), (livingEntity) -> {
- return true;
- }), this.avoidEntityTargeting, this.mob, this.mob.getX(), this.mob.getY(), this.mob.getZ());
+ this.toAvoid = this.nearbyTracker.getClosestEntity(this.mob.getBoundingBox().inflate(this.maxDist, 3.0D, this.maxDist), this.avoidEntityTargeting, this.mob.getX(), this.mob.getY(), this.mob.getZ()); // JettPack - lithium: ai.nearby_entity_tracking.goals
if (this.toAvoid == null) {
return false;
} else {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java
index 8b189d7587303263efa1790066e5a83edd45f9d7..352662385884e0c818961171b2c7f3ca5df00942 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java
@@ -8,6 +8,12 @@ import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import net.minecraft.world.entity.player.Player;
+// JettPack start
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker;
+import net.minecraft.core.Vec3i;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.EntityDimensions;
+// JettPack end
public class LookAtPlayerGoal extends Goal {
public static final float DEFAULT_PROBABILITY = 0.02F;
@@ -20,6 +26,7 @@ public class LookAtPlayerGoal extends Goal {
private final boolean onlyHorizontal;
protected final Class<? extends LivingEntity> lookAtType;
protected final TargetingConditions lookAtContext;
+ private NearbyEntityTracker<? extends LivingEntity> nearbyTracker; // JettPack
public LookAtPlayerGoal(Mob mob, Class<? extends LivingEntity> targetType, float range) {
this(mob, targetType, range, 0.02F);
@@ -43,6 +50,14 @@ public class LookAtPlayerGoal extends Goal {
} else {
this.lookAtContext = TargetingConditions.forNonCombat().range((double)range);
}
+ // JettPack start - lithium: ai.nearby_entity_tracking.goals
+ EntityDimensions dimensions = this.mob.getType().getDimensions();
+ double adjustedRange = dimensions.width * 0.5D + this.lookDistance + 2D;
+ int horizontalRange = Mth.ceil(adjustedRange);
+ this.nearbyTracker = new NearbyEntityTracker<>(targetType, mob, new Vec3i(horizontalRange, Mth.ceil(dimensions.height + 3 + 2), horizontalRange));
+
+ mob.addListener(this.nearbyTracker);
+ // JettPack end
}
@@ -56,11 +71,9 @@ public class LookAtPlayerGoal extends Goal {
}
if (this.lookAtType == Player.class) {
- this.lookAt = this.mob.level.getNearestPlayer(this.lookAtContext, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
+ this.lookAt = this.nearbyTracker.getClosestEntity(null, this.lookAtContext, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); // JettPack - lithium: ai.nearby_entity_tracking.goals
} else {
- this.lookAt = this.mob.level.getNearestEntity(this.mob.level.getEntitiesOfClass(this.lookAtType, this.mob.getBoundingBox().inflate((double)this.lookDistance, 3.0D, (double)this.lookDistance), (livingEntity) -> {
- return true;
- }), this.lookAtContext, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
+ this.lookAt = this.nearbyTracker.getClosestEntity(this.mob.getBoundingBox().inflate(this.lookDistance, 3.0D, this.lookDistance), this.lookAtContext, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); // JettPack - lithium: ai.nearby_entity_tracking.goals
}
return this.lookAt != null;
diff --git a/src/main/java/net/minecraft/world/level/entity/EntitySection.java b/src/main/java/net/minecraft/world/level/entity/EntitySection.java
index 13ebaf6aede402f5f702aae6c1e44445b00cd9bb..4bca54ab894b71f433d9f5318ed8363bea046983 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntitySection.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntitySection.java
@@ -12,6 +12,13 @@ import com.ishland.vmp.common.general.collections.ITypeFilterableList; // Mirai
import it.unimi.dsi.fastutil.objects.ObjectArrayList; // Mirai
import net.minecraft.world.level.entity.EntityAccess; // Mirai
import net.minecraft.world.level.entity.EntityTypeTest; // Mirai
+// JettPack start
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListener;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.SectionedEntityMovementTracker;
+import net.minecraft.world.entity.Entity;
+// JettPack end
public class EntitySection<T extends EntityAccess> {
private static final Logger LOGGER = LogUtils.getLogger();
@@ -21,6 +28,69 @@ public class EntitySection<T extends EntityAccess> {
public int itemCount;
public int inventoryEntityCount;
// Paper end
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ private final ReferenceOpenHashSet<NearbyEntityListener> nearbyEntityListeners = new ReferenceOpenHashSet<>(0);
+ private final ReferenceOpenHashSet<SectionedEntityMovementTracker<?, ?>> sectionVisibilityListeners = new ReferenceOpenHashSet<>(0);
+ private final long[] lastEntityMovementByType = new long[EntityTrackerEngine.NUM_MOVEMENT_NOTIFYING_CLASSES];
+ private long pos;
+
+ public void addListener(NearbyEntityListener listener) {
+ this.nearbyEntityListeners.add(listener);
+ if (this.chunkStatus.isAccessible()) {
+ listener.onSectionEnteredRange(this, this.storage);
+ }
+ }
+
+ public void removeListener(EntitySectionStorage<?> sectionedEntityCache, NearbyEntityListener listener) {
+ boolean removed = this.nearbyEntityListeners.remove(listener);
+ if (this.chunkStatus.isAccessible() && removed) {
+ listener.onSectionLeftRange(this, this.storage);
+ }
+ if (this.isEmpty()) {
+ sectionedEntityCache.remove(this.pos);
+ }
+ }
+
+ public void addListener(SectionedEntityMovementTracker<?, ?> listener) {
+ this.sectionVisibilityListeners.add(listener);
+ if (this.chunkStatus.isAccessible()) {
+ listener.onSectionEnteredRange(this);
+ }
+ }
+
+ public void removeListener(EntitySectionStorage<?> sectionedEntityCache, SectionedEntityMovementTracker<?, ?> listener) {
+ boolean removed = this.sectionVisibilityListeners.remove(listener);
+ if (this.chunkStatus.isAccessible() && removed) {
+ listener.onSectionLeftRange(this);
+ }
+ if (this.isEmpty()) {
+ sectionedEntityCache.remove(this.pos);
+ }
+ }
+
+ public void updateMovementTimestamps(int notificationMask, long time) {
+ long[] lastEntityMovementByType = this.lastEntityMovementByType;
+ int size = lastEntityMovementByType.length;
+ int mask;
+ for (int i = Integer.numberOfTrailingZeros(notificationMask); i < size; ) {
+ lastEntityMovementByType[i] = time;
+ mask = 0xffff_fffe << i;
+ i = Integer.numberOfTrailingZeros(notificationMask & mask);
+ }
+ }
+
+ public long[] getMovementTimestampArray() {
+ return this.lastEntityMovementByType;
+ }
+
+ public void setPos(long chunkSectionPos) {
+ this.pos = chunkSectionPos;
+ }
+
+ public long getPos() {
+ return this.pos;
+ }
+ // JettPack end
public EntitySection(Class<T> entityClass, Visibility status) {
this.chunkStatus = status;
@@ -36,6 +106,16 @@ public class EntitySection<T extends EntityAccess> {
}
// Paper end
this.storage.add(entity);
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ if (!this.chunkStatus.isAccessible() || this.nearbyEntityListeners.isEmpty()) {
+ return;
+ }
+ if (entity instanceof Entity entity1) {
+ for (NearbyEntityListener nearbyEntityListener : this.nearbyEntityListeners) {
+ nearbyEntityListener.onEntityEnteredRange(entity1);
+ }
+ }
+ // JettPack end
}
public boolean remove(T entity) {
@@ -46,6 +126,13 @@ public class EntitySection<T extends EntityAccess> {
this.inventoryEntityCount--;
}
// Paper end
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ if (this.chunkStatus.isAccessible() && !this.nearbyEntityListeners.isEmpty() && entity instanceof Entity entity1) {
+ for (NearbyEntityListener nearbyEntityListener : this.nearbyEntityListeners) {
+ nearbyEntityListener.onEntityLeftRange(entity1);
+ }
+ }
+ // JettPack end
return this.storage.remove(entity);
}
@@ -107,6 +194,7 @@ public class EntitySection<T extends EntityAccess> {
// Mirai end
public boolean isEmpty() {
+ if (!this.nearbyEntityListeners.isEmpty() || !this.sectionVisibilityListeners.isEmpty()) return false; // JettPack
return this.storage.isEmpty();
}
@@ -119,6 +207,33 @@ public class EntitySection<T extends EntityAccess> {
}
public Visibility updateChunkStatus(Visibility status) {
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ if (this.chunkStatus.isAccessible() != status.isAccessible()) {
+ if (!status.isAccessible()) {
+ if (!this.nearbyEntityListeners.isEmpty()) {
+ for (NearbyEntityListener nearbyEntityListener : this.nearbyEntityListeners) {
+ nearbyEntityListener.onSectionLeftRange(this, this.storage);
+ }
+ }
+ if (!this.sectionVisibilityListeners.isEmpty()) {
+ for (SectionedEntityMovementTracker<?, ?> listener : this.sectionVisibilityListeners) {
+ listener.onSectionLeftRange(this);
+ }
+ }
+ } else {
+ if (!this.nearbyEntityListeners.isEmpty()) {
+ for (NearbyEntityListener nearbyEntityListener : this.nearbyEntityListeners) {
+ nearbyEntityListener.onSectionEnteredRange(this, this.storage);
+ }
+ }
+ if (!this.sectionVisibilityListeners.isEmpty()) {
+ for (SectionedEntityMovementTracker<?, ?> listener : this.sectionVisibilityListeners) {
+ listener.onSectionEnteredRange(this);
+ }
+ }
+ }
+ }
+ // JettPack end
Visibility visibility = this.chunkStatus;
this.chunkStatus = status;
return visibility;
diff --git a/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java b/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
index ad12d9660cd57d147859a0e3e123c5b87c8e9748..df076e32d98813f94d68f9fee9afc90e4eeb11fd 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
@@ -20,6 +20,10 @@ import net.minecraft.core.SectionPos;
import net.minecraft.util.VisibleForDebug;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.AABB;
+// JettPack start
+import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.SectionedEntityMovementTracker;
+// JettPack end
public class EntitySectionStorage<T extends EntityAccess> {
private final Class<T> entityClass;
@@ -103,6 +107,20 @@ public class EntitySectionStorage<T extends EntityAccess> {
}
// Mirai end
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ private final Object2ReferenceOpenHashMap<SectionedEntityMovementTracker<?, ?>, SectionedEntityMovementTracker<?, ?>> sectionEntityMovementTrackers = new Object2ReferenceOpenHashMap<>();
+
+ public void remove(SectionedEntityMovementTracker<?, ?> tracker) {
+ this.sectionEntityMovementTrackers.remove(tracker);
+ }
+
+ public <S extends SectionedEntityMovementTracker<?, ?>> S deduplicate(S tracker) {
+ //noinspection unchecked
+ S storedTracker = (S) this.sectionEntityMovementTrackers.putIfAbsent(tracker, tracker);
+ return storedTracker == null ? tracker : storedTracker;
+ }
+ // JettPack end
+
public LongStream getExistingSectionPositionsInChunk(long chunkPos) {
int i = ChunkPos.getX(chunkPos);
int j = ChunkPos.getZ(chunkPos);
@@ -130,7 +148,11 @@ public class EntitySectionStorage<T extends EntityAccess> {
}
public EntitySection<T> getOrCreateSection(long sectionPos) {
- return this.sections.computeIfAbsent(sectionPos, this::createSection);
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ EntitySection<T> section = this.sections.computeIfAbsent(sectionPos, this::createSection);
+ section.setPos(sectionPos);
+ return section;
+ // JettPack end
}
@Nullable
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 30c4974b4019d67cffabd3e686c782659def3ba6..93d71c17c9904b92c81c4f93bc35a493803280ef 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -35,6 +35,10 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.EntityStorage;
import org.bukkit.craftbukkit.event.CraftEventFactory;
// CraftBukkit end
+// JettPack start
+import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListenerMulti;
+// JettPack end
public class PersistentEntitySectionManager<T extends EntityAccess> implements AutoCloseable {
@@ -61,6 +65,12 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
this.entityGetter = new LevelEntityGetterAdapter<>(this.visibleEntityStorage, this.sectionStorage);
}
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ public EntitySectionStorage<T> getCache() {
+ return this.sectionStorage;
+ }
+ // JettPack end
+
// CraftBukkit start - add method to get all entities in chunk
public List<Entity> getEntities(ChunkPos chunkCoordIntPair) {
return this.sectionStorage.getExistingSectionsInChunk(chunkCoordIntPair.toLong()).flatMap(EntitySection::getEntities).map(entity -> (Entity) entity).collect(Collectors.toList());
@@ -177,6 +187,16 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
entitysection.add(entity);
this.entitySliceManager.addEntity((Entity)entity); // Paper
entity.setLevelCallback(new PersistentEntitySectionManager.Callback(entity, i, entitysection));
+ // JettPack start - lithium: ai.nearby_entity_tracking
+ NearbyEntityListenerMulti listener = ((Entity)entity).getListener();
+ if (listener != null) {
+ listener.forEachChunkInRangeChange(
+ this.sectionStorage,
+ null,
+ SectionPos.of(entity.blockPosition())
+ );
+ }
+ // JettPack end
if (!existing) {
this.callbacks.onCreated(entity);
}
@@ -519,12 +539,25 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
private final T entity;
private long currentSectionKey;
private EntitySection<T> currentSection;
+ private int notificationMask; // JettPack