From 5d663b4d36750b60f2c4ca550d76f7b0fd06fe83 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 29 Jun 2025 18:44:04 +0900 Subject: [PATCH] optimize collectSpawningChunks (#382) * optimize collectSpawningChunks * add bit set * cleanup * cleanup * cleanup * cleanup --- .../features/0262-optimize-mob-spawning.patch | 85 ++++++++-- .../features/0269-optimize-random-tick.patch | 10 +- .../features/0272-Paw-optimization.patch | 23 +-- .../leaf/world/NatureSpawnChunkMap.java | 154 ++++++++++++++++++ 4 files changed, 229 insertions(+), 43 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/world/NatureSpawnChunkMap.java diff --git a/leaf-server/minecraft-patches/features/0262-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0262-optimize-mob-spawning.patch index 2169e367..51061420 100644 --- a/leaf-server/minecraft-patches/features/0262-optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0262-optimize-mob-spawning.patch @@ -26,7 +26,7 @@ index 79674f4bd7a12c42dec19a4175012d7a2dc88b84..0a97a491737807d59815b75635fa3d8c } // Paper end - Optional per player mob spawns diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 46e171ca454253c32e22c0c18587e9a7ba19f331..d91d6988268174ea9f2c919b57a6ba7ea00d7066 100644 +index 46e171ca454253c32e22c0c18587e9a7ba19f331..1c7e2e5124c7d4c1b86039b0327bfd92c49df9b1 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -70,11 +70,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -47,7 +47,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..d91d6988268174ea9f2c919b57a6ba7e } private void tickChunks() { -+ // Leaf start ++ // Leaf start - optimize mob spawning + if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled && this.level.tickRateManager().runsNormally()) { + for (ServerPlayer player : this.level.players) { + // Paper start - per player mob spawning backoff @@ -63,7 +63,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..d91d6988268174ea9f2c919b57a6ba7e + // Paper end - per player mob spawning backoff + } + } -+ // Leaf end ++ // Leaf end - optimize mob spawning long gameTime = this.level.getGameTime(); long l = gameTime - this.lastInhabitedUpdate; this.lastInhabitedUpdate = gameTime; @@ -108,32 +108,81 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..d91d6988268174ea9f2c919b57a6ba7e mobCapCalculator, // This is the key fix - was previously null level.paperConfig().entities.spawning.perPlayerMobSpawns ); -@@ -627,9 +644,23 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - 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 +@@ -610,6 +627,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + chunkRange = Math.min(chunkRange, 8); + entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange); + entityPlayer.playerNaturallySpawnedEvent.callEvent(); ++ this.level.natureSpawnChunkMap.addPlayer(entityPlayer); // Leaf - optimize mob spawning + } + // Paper end - PlayerNaturallySpawnCreaturesEvent + boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit +@@ -621,16 +639,40 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + List list = this.spawningChunks; + + try { +- this.chunkMap.collectSpawningChunks(list); ++ // Leaf start - optimize mob spawning ++ // this.chunkMap.collectSpawningChunks(list); ++ this.level.natureSpawnChunkMap.build(); + // Paper start - chunk tick iteration optimisation +- 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 ++ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ this.level.natureSpawnChunkMap.collectSpawningChunks(this.level.moonrise$getPlayerTickingChunks(), list); ++ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used ++ 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 - for (LevelChunk levelChunk : list) { - this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // Pufferfish -+ // Leaf start - optimize mob spawning -+ var lastSpawnState1 = lastSpawnState; -+ if ((!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && lastSpawnState1 != null) { -+ lastSpawnState1.applyPerPlayerMobCount(level); -+ if (list instanceof it.unimi.dsi.fastutil.objects.ReferenceArrayList arrayList) { -+ LevelChunk[] levelChunks = arrayList.elements(); -+ int size = arrayList.size(); -+ for (int i = 0; i < size; i++) { -+ this.tickSpawningChunk(levelChunks[i], timeInhabited, filteredSpawningCategories, lastSpawnState1); // Pufferfish -+ } -+ } else { -+ for (LevelChunk levelChunk : list) { -+ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState1); // Pufferfish ++ if (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) { ++ NaturalSpawner.SpawnState currentState = lastSpawnState; ++ if (currentState != null) { ++ currentState.applyPerPlayerMobCount(level); ++ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ for (LevelChunk levelChunk : list) { ++ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, currentState); // Pufferfish ++ } ++ } else { ++ ca.spottedleaf.moonrise.common.list.ReferenceList chunks = this.level.moonrise$getPlayerTickingChunks(); ++ LevelChunk[] raw = chunks.getRawDataUnchecked(); ++ for (int i = 0, length = chunks.size(); i < length; i++) { ++ LevelChunk levelChunk = raw[i]; ++ if (level.natureSpawnChunkMap.contains(levelChunk.locX, levelChunk.locZ)) { ++ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, currentState); // Pufferfish ++ } ++ } + } + } } + // Leaf end - optimize mob spawning } finally { ++ this.level.natureSpawnChunkMap.clear(); // Leaf - optimize mob spawning list.clear(); } + +@@ -648,7 +690,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + + if (!spawnCategories.isEmpty()) { +- if (this.level.getWorldBorder().isWithinBounds(pos) && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { // Paper - rewrite chunk system // Pufferfish ++ if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system + NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories); + } + } +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index fc61a7083232148397a0b330ce2a4d70ba38543d..d13ab16368d5fed5c81211b63c7670e534f6ed01 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1114,6 +1114,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking + ++ public org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting + ChunkPos pos = chunk.getPos(); diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java index 81e176d17fb072f9ee531639abfe42134ae833a9..ab6fa7ed111ef16a0b6774c21988589ee2110c66 100644 --- a/net/minecraft/world/level/NaturalSpawner.java diff --git a/leaf-server/minecraft-patches/features/0269-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0269-optimize-random-tick.patch index 3fcb38cd..c92d82ae 100644 --- a/leaf-server/minecraft-patches/features/0269-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0269-optimize-random-tick.patch @@ -5,10 +5,10 @@ Subject: [PATCH] optimize random tick diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index d91d6988268174ea9f2c919b57a6ba7ea00d7066..804ad5ae82b875d3b2cccb828eaa0ea54fe43011 100644 +index f3489fda7f144e13e23fdb4521c942489238fe75..76c0b36025e2c12f990b9d4ec7c1d88b227c333b 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -665,7 +665,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon +@@ -676,7 +676,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon list.clear(); } @@ -24,13 +24,13 @@ index d91d6988268174ea9f2c919b57a6ba7ea00d7066..804ad5ae82b875d3b2cccb828eaa0ea5 this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index fc61a7083232148397a0b330ce2a4d70ba38543d..d34a7cfa3b923d59ade35ce4c43cfd3f1477e057 100644 +index d13ab16368d5fed5c81211b63c7670e534f6ed01..c49d2f7314a35763831285d7d46d34dade2f0085 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1114,6 +1114,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - +@@ -1115,6 +1115,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking + public org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning + public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick public void tickChunk(LevelChunk chunk, int randomTickSpeed) { final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting diff --git a/leaf-server/minecraft-patches/features/0272-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0272-Paw-optimization.patch index 0b6999ff..40361ffd 100644 --- a/leaf-server/minecraft-patches/features/0272-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0272-Paw-optimization.patch @@ -99,28 +99,11 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194 - } - // Paper end - detailed watchdog information } -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 804ad5ae82b875d3b2cccb828eaa0ea54fe43011..328ed610f5d532784bd6c38c4833591dc19442cb 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -640,8 +640,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - try { - this.chunkMap.collectSpawningChunks(list); - // Paper start - chunk tick iteration optimisation -- 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 -+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used -+ 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 - - // Leaf start - optimize mob spawning diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index d34a7cfa3b923d59ade35ce4c43cfd3f1477e057..e5ec54af005eb53399f9d2db49917c24023fcfdc 100644 +index c49d2f7314a35763831285d7d46d34dade2f0085..79f2b26bc466238ec83cd049f38d257b0000028d 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1517,13 +1517,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1518,13 +1518,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - log detailed entity tick information public void tickNonPassenger(Entity entity) { @@ -134,7 +117,7 @@ index d34a7cfa3b923d59ade35ce4c43cfd3f1477e057..e5ec54af005eb53399f9d2db49917c24 entity.setOldPosAndRot(); entity.tickCount++; entity.totalEntityAge++; // Paper - age-like counter for all entities -@@ -1536,13 +1530,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1537,13 +1531,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe for (Entity entity1 : entity.getPassengers()) { this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/NatureSpawnChunkMap.java b/leaf-server/src/main/java/org/dreeam/leaf/world/NatureSpawnChunkMap.java new file mode 100644 index 00000000..354d71c9 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/NatureSpawnChunkMap.java @@ -0,0 +1,154 @@ +package org.dreeam.leaf.world; + +import ca.spottedleaf.moonrise.common.list.ReferenceList; +import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; +import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.LevelChunk; + +import java.util.List; + +public class NatureSpawnChunkMap { + /// breadth-first search + /// + /// 0 4 12 28 48 80 112 148 196 + private static final long[][] TABLE_BFS = new long[][]{ + {0L}, + {0L, 4294967295L, -4294967296L, 4294967296L, 1L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, 4294967294L, -8589934592L, 8589934592L, 2L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, 4294967293L, -12884901888L, 12884901888L, 3L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 4294967292L, -17179869184L, 17179869184L, 4L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 4294967291L, -21474836480L, 21474836480L, 5L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -8589934597L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 4294967290L, -25769803776L, 25769803776L, 6L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -12884901893L, -8589934597L, -17179869188L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 21474836475L, 25769803772L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -21474836476L, 21474836484L, -17179869179L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 17179869189L, -8589934598L, -4294967302L, -21474836483L, -21474836482L, -6L, 4294967290L, -21474836481L, -25769803776L, 8589934586L, 12884901882L, -25769803775L, -25769803774L, 17179869178L, -25769803773L, 30064771069L, 30064771070L, 30064771071L, 25769803776L, 25769803777L, 25769803778L, 25769803779L, -12884901882L, -8589934586L, -4294967290L, 6L, 4294967302L, 8589934598L, 12884901894L, 4294967289L, -30064771072L, 30064771072L, 7L}, + {0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -17179869189L, -12884901893L, -8589934597L, -17179869188L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 21474836475L, 25769803771L, 25769803772L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -21474836476L, 21474836484L, -21474836475L, -17179869179L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 17179869189L, 21474836485L, -17179869190L, -12884901894L, -21474836485L, -21474836484L, -8589934598L, -4294967302L, -21474836483L, -21474836482L, -6L, 4294967290L, -21474836481L, -25769803776L, 8589934586L, 12884901882L, -25769803775L, -25769803774L, 17179869178L, 21474836474L, -25769803773L, -25769803772L, 25769803770L, 30064771067L, 30064771068L, 30064771069L, 30064771070L, 30064771071L, 25769803776L, 25769803777L, 25769803778L, 25769803779L, 25769803780L, -25769803771L, 25769803781L, -21474836474L, -17179869178L, -12884901882L, -8589934586L, -4294967290L, 6L, 4294967302L, 8589934598L, 12884901894L, 17179869190L, 21474836486L, -8589934599L, -25769803779L, -4294967303L, -7L, -25769803778L, -25769803777L, 4294967289L, 8589934585L, -30064771072L, -30064771071L, 12884901881L, 17179869177L, -30064771070L, -30064771069L, 34359738365L, 34359738366L, 34359738367L, 30064771072L, 30064771073L, 30064771074L, 30064771075L, -12884901881L, -8589934585L, -4294967289L, 7L, 4294967303L, 8589934599L, 12884901895L, 4294967288L, -34359738368L, 34359738368L, 8L} + }; + private static final int MAX_RADIUS = 8; + private static final int SIZE_RADIUS = 9; + private static final int REGION_MASK = 15; + private static final int REGION_SHIFT = 4; + + private final LongArrayList[] centersByRadius; + private final Long2LongOpenHashMap regionBitSets; + + public NatureSpawnChunkMap() { + this.centersByRadius = new LongArrayList[SIZE_RADIUS]; + for (int i = 0; i < SIZE_RADIUS; i++) { + this.centersByRadius[i] = new LongArrayList(); + } + this.regionBitSets = new Long2LongOpenHashMap(); + } + + public void clear() { + for (LongArrayList chunkPosition : this.centersByRadius) { + chunkPosition.clear(); + } + this.regionBitSets.clear(); + } + + public void addPlayer(ServerPlayer player) { + if (player.isSpectator()) { + return; + } + PlayerNaturallySpawnCreaturesEvent event = player.playerNaturallySpawnedEvent; + if (event == null || event.isCancelled()) { + return; + } + int range = event.getSpawnRadius(); + if (range > MAX_RADIUS) { + range = MAX_RADIUS; + } else if (range < 0) { + return; + } + this.centersByRadius[range].add(player.chunkPosition().longKey); + } + + public void build() { + for (int index = 0; index < SIZE_RADIUS; index++) { + buildBy(index); + } + } + + private void buildBy(int index) { + LongArrayList list = this.centersByRadius[index]; + int centersSize = deduplicate(list); + if (centersSize == 0) { + return; + } + long[] centersRaw = list.elements(); + long cachedKey = ChunkPos.asLong(ChunkPos.getX(centersRaw[0]) >> REGION_SHIFT, ChunkPos.getZ(centersRaw[0]) >> REGION_SHIFT); + long cachedVal = this.regionBitSets.get(cachedKey); + long[] offsets = TABLE_BFS[index]; + for (int i = 0; i < centersSize; i++) { + long center = centersRaw[i]; + int cx = ChunkPos.getX(center); + int cz = ChunkPos.getZ(center); + + for (long packedOffset : offsets) { + int dx = ChunkPos.getX(packedOffset); + int dz = ChunkPos.getZ(packedOffset); + int chunkX = cx + dx; + int chunkZ = cz + dz; + + int regionX = chunkX >> REGION_SHIFT; + int regionZ = chunkZ >> REGION_SHIFT; + long regionKey = ChunkPos.asLong(regionX, regionZ); + + int localX = chunkX & REGION_MASK; + int localZ = chunkZ & REGION_MASK; + int bitIndex = (localZ << REGION_SHIFT) | localX; + long bitMask = 1L << bitIndex; + + if (regionKey != cachedKey) { + this.regionBitSets.put(cachedKey, cachedVal); + cachedKey = regionKey; + cachedVal = this.regionBitSets.get(regionKey); + } + + cachedVal |= bitMask; + } + } + + if (cachedVal != 0L) { + this.regionBitSets.put(cachedKey, cachedVal); + } + } + + private int deduplicate(LongArrayList list) { + int n = list.size(); + if (n == 0) { + return 0; + } + list.unstableSort(null); + long[] centersRaw = list.elements(); + int size = 0; + for (int i = 1; i < n; i++) { + long current = centersRaw[i]; + long last = centersRaw[size]; + if (current != last) { + size++; + centersRaw[size] = current; + } + } + return size + 1; + } + + public void collectSpawningChunks(ReferenceList chunks, List out) { + LevelChunk[] raw = chunks.getRawDataUnchecked(); + for (int i = 0, length = chunks.size(); i < length; i++) { + LevelChunk chunk = raw[i]; + if (contains(chunk.locX, chunk.locZ)) { + out.add(chunk); + } + } + } + + public boolean contains(int chunkX, int chunkZ) { + int regionX = chunkX >> REGION_SHIFT; + int regionZ = chunkZ >> REGION_SHIFT; + long bitset = this.regionBitSets.get(ChunkPos.asLong(regionX, regionZ)); + return bitset != 0 && (bitset & (1L << (((chunkZ & REGION_MASK) << REGION_SHIFT) | (chunkX & REGION_MASK)))) != 0L; + } +}