mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-26 18:39:23 +00:00
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2 since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList. Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2 lazy handles filter condition on iteration, so much better.
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 e5a10873e2ee8e4f75861eee46dec16c701c086f..47e714fc564898f9a04a89a6b1b72f4c3adce2a0 100644
|
|
--- a/net/minecraft/server/MinecraftServer.java
|
|
+++ b/net/minecraft/server/MinecraftServer.java
|
|
@@ -286,6 +286,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) {
|
|
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
|
|
@@ -1049,6 +1050,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 9c8d88e81ad9d06966bcec0ef134c8e658b9b51e..ca1406178cfb1a5a10947872585e2dcf3059b460 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 e53440bd5f0e659db0745a009540520f6dc41238..4979171c8da6f366aab636fb3058ca457fe75061 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,
|
|
@@ -507,6 +509,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
|
|
@@ -525,6 +575,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
|
|
@@ -539,12 +590,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;
|
|
@@ -558,7 +613,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();
|
|
}
|
|
@@ -573,7 +628,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();
|
|
@@ -593,7 +648,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..0d64c990730af37abaca10b7c9f840342a0ee7bd 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<>(org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled); // Paper - rewrite chunk system // Pufferfish - private->public and do thread check
|
|
|
|
private void ensureActiveIsNotIterated() {
|
|
// Paper - rewrite chunk system
|