9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0096-Pufferfish-Optimize-mob-spawning.patch
2025-06-11 05:20:38 +08:00

193 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kevin Raneri <kevin.raneri@gmail.com>
Date: Wed, 10 Nov 2021 00:37:03 -0500
Subject: [PATCH] Pufferfish: Optimize mob spawning
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
Co-authored-by: booky10 <boooky10@gmail.com>
This patch aims to reduce the main-thread impact of mob spawning by
offloading as much work as possible to other threads. It is possible for
inconsistencies to come up, but when they happen they never interfere
with the server's operation (they don't produce errors), and side
effects are limited to more or less mobs being spawned in any particular
tick.
It is possible to disable this optimization if it is not required or if
it interferes with any plugins. On servers with thousands of entities,
this can result in performance gains of up to 15%, which is significant
and, in my opinion, worth the low risk of minor mob-spawning-related
inconsistencies.
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index c1b3d2b4c56b657ff51c8b27b4b80e0c0d01ddbe..18a0bf81562f61fd6ede72d51d836ae28e9226c3 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -285,6 +285,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
private final Set<String> pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
+ public gg.pufferfish.pufferfish.util.AsyncExecutor mobSpawnExecutor = new gg.pufferfish.pufferfish.util.AsyncExecutor("MobSpawning"); // Pufferfish - optimize mob spawning
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -1046,6 +1047,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end - rewrite chunk system
// Paper start - Improved watchdog support - move final shutdown items here
Util.shutdownExecutors();
+ org.dreeam.leaf.async.ShutdownExecutors.shutdown(this); // Leaf
try {
net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
} catch (final Exception ignored) {
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index cd266af35c7b8455af19adc9b0874cdd7572217f..349eafa321c955c6bda7a5aa6931311d85867565 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -320,6 +320,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
LOGGER.info("JMX monitoring enabled");
}
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) mobSpawnExecutor.start(); // Pufferfish
+
return true;
}
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 3d7d336c2043b533012ee390e6da02ac4f64e1f2..e67196b815dad6baa823822285cff83848e549ce 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -182,6 +182,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Paper end - chunk tick iteration optimisations
+ public boolean firstRunSpawnCounts = true; // Pufferfish
+ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs
public ServerChunkCache(
ServerLevel level,
@@ -506,6 +508,54 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
this.broadcastChangedChunks(); // Gale - Purpur - remove vanilla profiler
}
+
+ // Pufferfish start - optimize mob spawning
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) {
+ 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;
+ }
+ // Paper end - per player mob spawning backoff
+ }
+ if (firstRunSpawnCounts) {
+ firstRunSpawnCounts = false;
+ _pufferfish_spawnCountsReady.set(true);
+ }
+ if (_pufferfish_spawnCountsReady.getAndSet(false)) {
+ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
+ int mapped = distanceManager.getNaturalSpawnChunkCount();
+ ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator =
+ level.entityTickList.entities.iterator(ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
+ try {
+ gg.pufferfish.pufferfish.util.IterableWrapper<Entity> wrappedIterator =
+ new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator);
+ // Fix: Use proper mob cap calculator based on configuration
+ LocalMobCapCalculator mobCapCalculator = !level.paperConfig().entities.spawning.perPlayerMobSpawns ?
+ new LocalMobCapCalculator(chunkMap) : null;
+
+ // This ensures the caps are properly enforced by using the correct calculator
+ lastSpawnState = NaturalSpawner.createState(
+ mapped,
+ wrappedIterator,
+ ServerChunkCache.this::getFullChunk,
+ mobCapCalculator, // This is the key fix - was previously null
+ level.paperConfig().entities.spawning.perPlayerMobSpawns
+ );
+ } finally {
+ objectiterator.finishedIterating();
+ }
+ _pufferfish_spawnCountsReady.set(true);
+ });
+ }
+ }
+ // Pufferfish end
}
private void broadcastChangedChunks() { // Gale - Purpur - remove vanilla profiler
@@ -524,6 +574,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
// Paper start - Optional per player mob spawns
NaturalSpawner.SpawnState spawnState;
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
+ if (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) { // Pufferfish - moved down when async processing
// re-set mob counts
for (ServerPlayer player : this.level.players) {
// Paper start - per player mob spawning backoff
@@ -538,12 +589,16 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Paper end - per player mob spawning backoff
}
- spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
+ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); // Pufferfish - async mob spawning
+ } // Pufferfish - (endif) moved down when async processing
} else {
- spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ // Pufferfish start - async mob spawning
+ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ _pufferfish_spawnCountsReady.set(true);
+ // Pufferfish end
}
// Paper end - Optional per player mob spawns
- this.lastSpawnState = spawnState;
+ //this.lastSpawnState = spawnState; // Pufferfish - this is managed asynchronously
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
List<MobCategory> filteredSpawningCategories;
@@ -557,7 +612,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 // Pufferfish
} else {
filteredSpawningCategories = List.of();
}
@@ -572,7 +627,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); // Pufferfish
}
} finally {
list.clear();
@@ -592,7 +647,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
if (!spawnCategories.isEmpty()) {
- if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system
+ if (this.level.getWorldBorder().isWithinBounds(pos) && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { // Paper - rewrite chunk system // Pufferfish
NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
}
}
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index 423779a2b690f387a4f0bd07b97b50e0baefda76..dec51066fc3f57b7bdc56195313c219f45a7fbee 100644
--- a/net/minecraft/world/level/entity/EntityTickList.java
+++ b/net/minecraft/world/level/entity/EntityTickList.java
@@ -9,7 +9,7 @@ import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
public class EntityTickList {
- private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system
+ public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system // Pufferfish - private->public
private void ensureActiveIsNotIterated() {
// Paper - rewrite chunk system