Skip to content

Commit 2a7dfb6

Browse files
committed
Define POI search order explicitly
1 parent 27b00ac commit 2a7dfb6

File tree

3 files changed

+285
-92
lines changed

3 files changed

+285
-92
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package net.caffeinemc.mods.lithium.common.world.interests;
2+
3+
import net.minecraft.core.BlockPos;
4+
import net.minecraft.core.SectionPos;
5+
import net.minecraft.world.entity.ai.village.poi.PoiManager;
6+
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
7+
import net.minecraft.world.entity.ai.village.poi.PoiSection;
8+
import net.minecraft.world.level.ChunkPos;
9+
10+
import java.util.List;
11+
import java.util.Optional;
12+
13+
public interface PoiOrdering {
14+
15+
default boolean isOrdered(BlockPos center, PoiManager poiManager, List<BlockPos> positions) {
16+
if (!positions.isEmpty()) {
17+
BlockPos curr = positions.getFirst();
18+
for (int i = 1; i < positions.size(); i++) {
19+
BlockPos next = positions.get(i);
20+
int order = compare(center, poiManager, curr, next);
21+
if (order != 1) {
22+
return false;
23+
}
24+
curr = next;
25+
}
26+
}
27+
return true;
28+
}
29+
30+
default void checkOrderOrThrow(BlockPos center, PoiManager poiManager, List<BlockPos> positions) {
31+
if (!positions.isEmpty()) {
32+
BlockPos curr = positions.getFirst();
33+
for (int i = 1; i < positions.size(); i++) {
34+
BlockPos next = positions.get(i);
35+
int order = compare(center, poiManager, curr, next);
36+
if (order != 1) {
37+
if (order == 0) {
38+
order = compare(center, poiManager, curr, next); //Line for debugging
39+
throw new IllegalStateException("Positions are equal at index " + i + ": " + curr + " == " + next);
40+
} else {
41+
order = compare(center, poiManager, curr, next); //Line for debugging
42+
throw new IllegalStateException("Positions are ordered incorrectly at index " + i + ": " + curr + " < " + next);
43+
}
44+
}
45+
curr = next;
46+
}
47+
}
48+
}
49+
50+
int compare(BlockPos center, PoiManager poiManager, BlockPos posA, BlockPos posB);
51+
52+
53+
record InChunk() implements PoiOrdering {
54+
55+
public static final InChunk INSTANCE = new InChunk();
56+
57+
@Override
58+
public int compare(BlockPos center, PoiManager poiManager, BlockPos posA, BlockPos posB) {
59+
int aChunkZ = posA.getZ() >> 4;
60+
int bChunkZ = posB.getZ() >> 4;
61+
62+
int orderZ = Integer.compare(bChunkZ, aChunkZ);
63+
if (orderZ != 0) {
64+
throw new IllegalStateException("Positions are not in the same chunk: " + posA + " in " + new ChunkPos(posA) + " vs " + posB + " in " + new ChunkPos(posB));
65+
}
66+
67+
int aChunkX = posA.getX() >> 4;
68+
int bChunkX = posB.getX() >> 4;
69+
70+
int orderX = Integer.compare(bChunkX, aChunkX);
71+
if (orderX != 0) {
72+
throw new IllegalStateException("Positions are not in the same chunk: " + posA + " in " + new ChunkPos(posA) + " vs " + posB + " in " + new ChunkPos(posB));
73+
}
74+
75+
int aChunkY = posA.getY() >> 4;
76+
int bChunkY = posB.getY() >> 4;
77+
78+
int orderY = Integer.compare(bChunkY, aChunkY);
79+
if (orderY != 0) {
80+
return orderY;
81+
}
82+
83+
Optional<PoiSection> poiSection = poiManager.getOrLoad(SectionPos.asLong(posA));
84+
if (poiSection.isEmpty()) {
85+
throw new IllegalStateException("PoiManager " + poiManager + " has no section at position " + posA);
86+
}
87+
PoiSection poiSection1 = poiSection.get();
88+
List<PoiRecord> retrievedRecords = poiSection1.getRecords(a -> true, PoiManager.Occupancy.ANY).filter(poiRecord -> poiRecord.getPos().equals(posA) || poiRecord.getPos().equals(posB)).toList();
89+
if (retrievedRecords.size() != 2) {
90+
throw new IllegalStateException("Expected two POI records at " + posA + ", " + posB + ", found " + retrievedRecords.size());
91+
}
92+
93+
if (retrievedRecords.getFirst().getPos().equals(posA)) {
94+
return 1;
95+
}
96+
if (retrievedRecords.getFirst().getPos().equals(posB)) {
97+
return -1;
98+
}
99+
100+
throw new IllegalStateException("Expected two differing poi positions matching " + posA + ", " + posB + ", found " + retrievedRecords);
101+
}
102+
}
103+
104+
record InSquare() implements PoiOrdering {
105+
106+
public static final InSquare INSTANCE = new InSquare();
107+
108+
@Override
109+
public int compare(BlockPos center, PoiManager poiManager, BlockPos posA, BlockPos posB) {
110+
int aChunkZ = posA.getZ() >> 4;
111+
int bChunkZ = posB.getZ() >> 4;
112+
113+
int orderZ = Integer.compare(bChunkZ, aChunkZ);
114+
if (orderZ != 0) {
115+
return orderZ;
116+
}
117+
118+
int aChunkX = posA.getX() >> 4;
119+
int bChunkX = posB.getX() >> 4;
120+
121+
int orderX = Integer.compare(bChunkX, aChunkX);
122+
if (orderX != 0) {
123+
return orderX;
124+
}
125+
126+
return InChunk.INSTANCE.compare(center, poiManager, posA, posB);
127+
}
128+
}
129+
130+
record L2ThenInSquare() implements PoiOrdering {
131+
132+
public static final L2ThenInSquare INSTANCE = new L2ThenInSquare();
133+
134+
@Override
135+
public int compare(BlockPos center, PoiManager poiManager, BlockPos posA, BlockPos posB) {
136+
double distASq = center.distSqr(posA);
137+
double distBSq = center.distSqr(posB);
138+
139+
int orderDist = Double.compare(distBSq, distASq);
140+
if (orderDist != 0) {
141+
return orderDist;
142+
}
143+
144+
return InSquare.INSTANCE.compare(center, poiManager, posA, posB);
145+
}
146+
}
147+
}

common/src/main/resources/lithium.accesswidener

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ accessible method net/minecraft/world/phys/shapes/Shapes findBits (DD)I
2222
accessible field net/minecraft/server/level/ChunkMap level Lnet/minecraft/server/level/ServerLevel;
2323
accessible field net/minecraft/world/level/chunk/Strategy LINEAR_PALETTE_FACTORY Lnet/minecraft/world/level/chunk/Palette$Factory;
2424
accessible field net/minecraft/world/level/chunk/Strategy SINGLE_VALUE_PALETTE_FACTORY Lnet/minecraft/world/level/chunk/Palette$Factory;
25-
accessible class net/minecraft/world/level/chunk/LevelChunk$RebindableTickingBlockEntityWrapper
25+
accessible class net/minecraft/world/level/chunk/LevelChunk$RebindableTickingBlockEntityWrapper
26+
accessible method net/minecraft/world/level/chunk/storage/SectionStorage getOrLoad (J)Ljava/util/Optional;

0 commit comments

Comments
 (0)