From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:58:39 +0300 Subject: [PATCH] Regionized Chunk Ticking diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java index 2678bf59d557f085c7265e2f3eb038647723d35e..f13ddc9a11ad0bc6396e31d7629f8ab9425cd4d7 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -56,6 +56,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon private static final Logger LOGGER = LogUtils.getLogger(); private final DistanceManager distanceManager; private final ServerLevel level; + // DivineMC - Regionized Chunk Ticking + public static final Executor REGION_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadCount, new org.bxteam.divinemc.util.NamedAgnosticThreadFactory<>("region_ticking", ca.spottedleaf.moonrise.common.util.TickThread::new, org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadPriority)); + public volatile int tickingRegionsCount = 0; + // DivineMC end - Regionized Chunk Ticking public final Thread mainThread; final ThreadedLevelLightEngine lightEngine; public final ServerChunkCache.MainThreadExecutor mainThreadProcessor; @@ -479,6 +483,46 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon } // CraftBukkit end + // DivineMC start - Regionized Chunk Ticking + private static final int[] DX = {1, -1, 0, 0, 1, -1, -1, 1}; + private static final int[] DZ = {0, 0, 1, -1, 1, 1, -1, -1}; + + private List[] splitChunksIntoRegions(List chunks) { + int size = chunks.size(); + java.util.IdentityHashMap chunkSet = new java.util.IdentityHashMap<>(size); + + for (LevelChunk chunk : chunks) { + chunkSet.put(chunk, Boolean.TRUE); + } + + List> groups = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(size >> 3); + LevelChunk[] stack = new LevelChunk[size]; + int stackPointer = 0; + + for (LevelChunk chunk : chunks) { + if (chunkSet.remove(chunk) == null) continue; + + List group = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(64); + stack[stackPointer++] = chunk; + + while (stackPointer > 0) { + LevelChunk current = stack[--stackPointer]; + group.add(current); + + for (int i = 0; i < 8; i++) { + LevelChunk neighbor = getChunk(current.locX + DX[i], current.locZ + DZ[i], false); + if (neighbor == null || chunkSet.remove(neighbor) == null) continue; + stack[stackPointer++] = neighbor; + } + } + + groups.add(group); + } + + return groups.toArray(new List[0]); + } + // DivineMC end - Regionized Chunk Ticking + @Override public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) { ProfilerFiller profilerFiller = Profiler.get(); @@ -519,7 +563,44 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon this.shuffleRandom.setSeed(this.level.random.nextLong()); if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled // Paper end - chunk tick iteration optimisation - this.tickChunks(profilerFiller, l, list); + // DivineMC start - Regionized Chunk Ticking + if (org.bxteam.divinemc.DivineConfig.enableRegionizedChunkTicking) { + List[] regions = splitChunksIntoRegions(list); + int regionCount = regions.length; + this.tickingRegionsCount = regionCount; + java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(regionCount); + + try { + java.util.concurrent.ForkJoinPool.managedBlock(new java.util.concurrent.ForkJoinPool.ManagedBlocker() { + @Override + public boolean block() throws InterruptedException { + for (List region : regions) { + if (region == null) continue; + REGION_EXECUTOR.execute(() -> { + try { + tickChunks(profilerFiller, l, region); + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + return true; + } + + @Override + public boolean isReleasable() { + return latch.getCount() == 0; + } + }); + } catch (InterruptedException ex) { + throw new RuntimeException("Interrupted managed block during region ticking", ex); + } + } else { + this.tickChunks(profilerFiller, l, list); + } + // DivineMC end - Regionized Chunk Ticking profilerFiller.pop(); } finally { list.clear();