mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
193 lines
12 KiB
Diff
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
|