From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Wed, 19 Mar 2025 23:24:32 +0300 Subject: [PATCH] Pufferfish: Optimize mob spawning Original license: GPL v3 Original project: https://github.com/pufferfish-gg/Pufferfish This patch reduces the main-thread impact of mob spawning by moving spawning work to other threads diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 4ba10f90711ac8b6813f86bcff9635716fdc6028..95e39a728b7505a273a6e9c927308ee8931ea756 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -288,6 +288,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + int mapped = distanceManager.getNaturalSpawnChunkCount(); + ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator objectiterator = level.entityTickList.entities.iterator(ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); + + try { + gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); + LocalMobCapCalculator mobCapCalculator = !level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(chunkMap) : null; + + lastSpawnState = NaturalSpawner.createState( + mapped, + wrappedIterator, + ServerChunkCache.this::getFullChunk, + mobCapCalculator, + level.paperConfig().entities.spawning.perPlayerMobSpawns + ); + } finally { + objectiterator.finishedIterating(); + } + spawnCountsReady.set(true); + }); + } + } + // DivineMC end - Pufferfish: Optimize mob spawning } private void broadcastChangedChunks() { @@ -518,27 +563,31 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount(); // Paper start - Optional per player mob spawns NaturalSpawner.SpawnState spawnState; + // DivineMC start - Pufferfish: Optimize mob spawning if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled - // re-set mob counts - for (ServerPlayer player : this.level.players) { - // Paper start - per player mob spawning backoff - for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { - player.mobCounts[ii] = 0; - - int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? - if (newBackoff < 0) { - newBackoff = 0; + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) { + // re-set mob counts + for (ServerPlayer player : this.level.players) { + // Paper start - per player mob spawning backoff + for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { + player.mobCounts[ii] = 0; + + int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? + if (newBackoff < 0) { + newBackoff = 0; + } + player.mobBackoffCounts[ii] = newBackoff; } - player.mobBackoffCounts[ii] = newBackoff; + // Paper end - per player mob spawning backoff } - // Paper end - per player mob spawning backoff + lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), true); } - spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); } else { - spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); + lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), false); + spawnCountsReady.set(true); } + // DivineMC end - Pufferfish: Optimize mob spawning // Paper end - Optional per player mob spawns - this.lastSpawnState = spawnState; boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); List filteredSpawningCategories; @@ -552,7 +601,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon } // 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 - filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnState, this.spawnFriendlies, this.spawnEnemies, flag, this.level); // CraftBukkit + filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag, this.level); // CraftBukkit // DivineMC - Pufferfish: Optimize mob spawning } else { filteredSpawningCategories = List.of(); } @@ -567,7 +616,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon // Paper end - chunk tick iteration optimisation for (LevelChunk levelChunk : list) { - this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, spawnState); + this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // DivineMC - Pufferfish: Optimize mob spawning } } finally { list.clear(); @@ -586,11 +635,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon this.level.tickThunder(chunk); } - if (!spawnCategories.isEmpty()) { - if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system - NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories); - } + // DivineMC start - Pufferfish: Optimize mob spawning + if (!spawnCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning || spawnCountsReady.get())) { // Paper - rewrite chunk system + NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories); } + // DivineMC end - Pufferfish: Optimize mob spawning } private void getFullChunk(long chunkPos, Consumer fullChunkGetter) {