Skip to content

Commit 49aecbf

Browse files
committed
perf(chunk): cache and reuse sorted chunk offsets for planning
1 parent 18abf4f commit 49aecbf

File tree

2 files changed

+94
-58
lines changed

2 files changed

+94
-58
lines changed

src/main/java/me/mapacheee/extendedhorizons/chunk/FakeChunkPlannerService.java

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
public class FakeChunkPlannerService {
1414

15+
private final ConcurrentHashMap<Long, long[]> sortedOffsetsCache = new ConcurrentHashMap<>();
16+
1517
public record PlanInput(
1618
int chunkX,
1719
int chunkZ,
@@ -39,20 +41,21 @@ public PlanResult build(PlanInput input) {
3941
int safeSquareRadius = (int) Math.floor(input.serverDistance() * input.safeSquareFactor());
4042
if (safeSquareRadius < 2) safeSquareRadius = 2;
4143

42-
Set<Long> neededChunks = new HashSet<>();
43-
for (int dx = -effectiveRadius; dx <= effectiveRadius; dx++) {
44-
for (int dz = -effectiveRadius; dz <= effectiveRadius; dz++) {
45-
int cx = chunkX + dx;
46-
int cz = chunkZ + dz;
47-
int chebyshev = Math.max(Math.abs(dx), Math.abs(dz));
48-
if (chebyshev <= safeSquareRadius) continue;
49-
if (dx * dx + dz * dz > effectiveRadius * effectiveRadius) continue;
50-
neededChunks.add(ChunkPos.asLong(cx, cz));
51-
}
44+
long[] sortedOffsets = getSortedOffsets(effectiveRadius, safeSquareRadius);
45+
int offsetCount = sortedOffsets.length;
46+
Set<Long> neededChunks = new HashSet<>(Math.max(16, offsetCount * 2));
47+
for (long packedOffset : sortedOffsets) {
48+
int dx = unpackX(packedOffset);
49+
int dz = unpackZ(packedOffset);
50+
neededChunks.add(ChunkPos.asLong(chunkX + dx, chunkZ + dz));
5251
}
5352

53+
Set<Long> sentChunks = input.sentChunksSnapshot();
54+
Set<Long> queuedSet = input.queuedSet();
55+
Set<Long> inflightSet = input.inflightSet();
56+
5457
Set<Long> chunksToUnload = new HashSet<>();
55-
for (Long chunkKey : input.sentChunksSnapshot()) {
58+
for (Long chunkKey : sentChunks) {
5659
if (!neededChunks.contains(chunkKey)) {
5760
chunksToUnload.add(chunkKey);
5861
}
@@ -63,32 +66,25 @@ public PlanResult build(PlanInput input) {
6366
int kept = 0;
6467
for (Long existing : input.currentQueue()) {
6568
if (!neededChunks.contains(existing)) continue;
66-
if (input.sentChunksSnapshot().contains(existing)) continue;
67-
if (input.inflightSet().contains(existing)) continue;
69+
if (sentChunks.contains(existing)) continue;
70+
if (inflightSet.contains(existing)) continue;
6871
rebuiltQueue.addLast(existing);
6972
rebuiltQueuedSet.add(existing);
7073
kept++;
7174
}
7275

73-
List<Long> toAdd = new ArrayList<>();
74-
for (Long chunkKey : neededChunks) {
75-
if (input.sentChunksSnapshot().contains(chunkKey)) continue;
76-
if (input.queuedSet().contains(chunkKey)) continue;
77-
if (input.inflightSet().contains(chunkKey)) continue;
76+
List<Long> toAdd = new ArrayList<>(offsetCount);
77+
for (long packedOffset : sortedOffsets) {
78+
int dx = unpackX(packedOffset);
79+
int dz = unpackZ(packedOffset);
80+
long chunkKey = ChunkPos.asLong(chunkX + dx, chunkZ + dz);
81+
if (sentChunks.contains(chunkKey)) continue;
82+
if (queuedSet.contains(chunkKey)) continue;
83+
if (inflightSet.contains(chunkKey)) continue;
7884
if (rebuiltQueuedSet.contains(chunkKey)) continue;
7985
toAdd.add(chunkKey);
8086
}
8187

82-
toAdd.sort(
83-
Comparator.comparingInt(
84-
key -> {
85-
int cx = ChunkPos.getX(key);
86-
int cz = ChunkPos.getZ(key);
87-
int dx = cx - chunkX;
88-
int dz = cz - chunkZ;
89-
return dx * dx + dz * dz;
90-
}));
91-
9288
return new PlanResult(
9389
safeSquareRadius,
9490
neededChunks,
@@ -98,4 +94,40 @@ public PlanResult build(PlanInput input) {
9894
toAdd,
9995
kept);
10096
}
97+
98+
private long[] getSortedOffsets(int effectiveRadius, int safeSquareRadius) {
99+
long cacheKey = (((long) effectiveRadius) << 32) | (safeSquareRadius & 0xFFFFFFFFL);
100+
return sortedOffsetsCache.computeIfAbsent(cacheKey, k -> buildSortedOffsets(effectiveRadius, safeSquareRadius));
101+
}
102+
103+
private long[] buildSortedOffsets(int effectiveRadius, int safeSquareRadius) {
104+
List<long[]> sortable = new ArrayList<>();
105+
for (int dx = -effectiveRadius; dx <= effectiveRadius; dx++) {
106+
for (int dz = -effectiveRadius; dz <= effectiveRadius; dz++) {
107+
int chebyshev = Math.max(Math.abs(dx), Math.abs(dz));
108+
if (chebyshev <= safeSquareRadius) continue;
109+
int distSq = dx * dx + dz * dz;
110+
if (distSq > effectiveRadius * effectiveRadius) continue;
111+
sortable.add(new long[] {pack(dx, dz), distSq});
112+
}
113+
}
114+
sortable.sort(Comparator.comparingLong(v -> v[1]));
115+
long[] offsets = new long[sortable.size()];
116+
for (int i = 0; i < sortable.size(); i++) {
117+
offsets[i] = sortable.get(i)[0];
118+
}
119+
return offsets;
120+
}
121+
122+
private static long pack(int x, int z) {
123+
return (((long) x) << 32) | (z & 0xFFFFFFFFL);
124+
}
125+
126+
private static int unpackX(long packed) {
127+
return (int) (packed >> 32);
128+
}
129+
130+
private static int unpackZ(long packed) {
131+
return (int) packed;
132+
}
101133
}

src/main/java/me/mapacheee/extendedhorizons/chunk/FakeChunkService.java

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -477,36 +477,40 @@ private void updatePlayerChunks(
477477

478478
dispatchService.processQueue(
479479
player, world, playerId, tracker, getSessionEpoch(playerId), dispatchHooks);
480-
String planDebug =
481-
"[EH] plan moved="
482-
+ moved
483-
+ " force="
484-
+ force
485-
+ " pos="
486-
+ chunkX
487-
+ ","
488-
+ chunkZ
489-
+ " target="
490-
+ viewDistance
491-
+ " server="
492-
+ serverDistance
493-
+ " safe="
494-
+ plan.safeSquareRadius()
495-
+ " needed="
496-
+ plan.neededChunks().size()
497-
+ " sent="
498-
+ tracker.getSentChunks().size()
499-
+ " unload="
500-
+ plan.chunksToUnload().size()
501-
+ " kept="
502-
+ plan.kept()
503-
+ " add="
504-
+ plan.toAdd().size()
505-
+ " queue="
506-
+ queue.size()
507-
+ " inflight="
508-
+ inflightCounts.computeIfAbsent(playerId, k -> new AtomicInteger(0)).get();
509-
debug(playerId, "plan", planDebug);
480+
if (config().debugEnabled()) {
481+
AtomicInteger inflight = inflightCounts.get(playerId);
482+
int inflightCount = inflight == null ? 0 : inflight.get();
483+
String planDebug =
484+
"[EH] plan moved="
485+
+ moved
486+
+ " force="
487+
+ force
488+
+ " pos="
489+
+ chunkX
490+
+ ","
491+
+ chunkZ
492+
+ " target="
493+
+ viewDistance
494+
+ " server="
495+
+ serverDistance
496+
+ " safe="
497+
+ plan.safeSquareRadius()
498+
+ " needed="
499+
+ plan.neededChunks().size()
500+
+ " sent="
501+
+ tracker.getSentChunks().size()
502+
+ " unload="
503+
+ plan.chunksToUnload().size()
504+
+ " kept="
505+
+ plan.kept()
506+
+ " add="
507+
+ plan.toAdd().size()
508+
+ " queue="
509+
+ queue.size()
510+
+ " inflight="
511+
+ inflightCount;
512+
debug(playerId, "plan", planDebug);
513+
}
510514
}
511515

512516
private void sendUnloadPacket(Player player, int x, int z) {

0 commit comments

Comments
 (0)