9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00
Files
LeavesMC/patches/server/0018-Optimize-mob-spawning.patch
2023-03-19 17:30:46 +08:00

191 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Sat, 13 Aug 2022 17:27:18 +0800
Subject: [PATCH] Optimize mob spawning
This patch is Powered by Pufferfish(https://github.com/pufferfish-gg/Pufferfish)
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index ad9c90b359874ee2c9a94c5b2cf72a634e8b56f4..4296153b171c827aadd0a3b70f1f13b8ba41cc65 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -314,6 +314,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public volatile boolean abnormalExit = false; // Paper
public boolean isIteratingOverLevels = false; // Paper
+ public top.leavesmc.leaves.util.AsyncExecutor mobSpawnExecutor = new top.leavesmc.leaves.util.AsyncExecutor("MobSpawning"); // Leaves - optimize mob spawning
+
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
AtomicReference<S> atomicreference = new AtomicReference();
Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 90d284192f27a69f6694544c885c3e4d393081bb..094af18dda115019430a3cbacaf5508f9ffa7690 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -342,6 +342,11 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
DedicatedServer.LOGGER.info("JMX monitoring enabled");
}
+ // Leaves start - optimize mob spawning
+ if (top.leavesmc.leaves.LeavesConfig.asyncMobSpawning) {
+ mobSpawnExecutor.start();
+ }
+ // Leaves end - optimize mob spawning
return true;
}
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index fbe209a66c77c47935ad026dd3e45e682af91fd8..31f6791935b3f4cc1349716448d46da2bcea2f15 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -345,7 +345,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
this.regionManagers.add(this.dataRegionManager);
// Paper end
- this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
+ this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new top.leavesmc.leaves.util.AsyncPlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper // Leaves - optimize mob spawning(actually not use)
// Paper start - use distance map to optimise entity tracker
this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length];
this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length];
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index ca84eddbdb1e198b899750e5f6b3eafd25ce970f..6a2adfe51b5ea6b3290349781f41ed7818f15fe5 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -77,6 +77,11 @@ public class ServerChunkCache extends ChunkSource {
private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4];
+ // Leaves start - optimize countmobs
+ public boolean firstRunSpawnCounts = true;
+ public final java.util.concurrent.atomic.AtomicBoolean spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false);
+ // Leaves end - optimize countmobs
+
private static int getChunkCacheKey(int x, int z) {
return x & 3 | ((z & 3) << 2);
}
@@ -710,20 +715,27 @@ public class ServerChunkCache extends ChunkSource {
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
int l = this.distanceManager.getNaturalSpawnChunkCount();
// Paper start - per player mob spawning
- NaturalSpawner.SpawnState spawnercreature_d; // moved down
+ // NaturalSpawner.SpawnState spawnercreature_d; // moved down // Leaves - asynchronously
if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled
- // re-set mob counts
- for (ServerPlayer player : this.level.players) {
- Arrays.fill(player.mobCounts, 0);
+ // Leaves start - moved down when async processing
+ if (!top.leavesmc.leaves.LeavesConfig.asyncMobSpawning) {
+ // re-set mob counts
+ for (ServerPlayer player : this.level.players) {
+ Arrays.fill(player.mobCounts, 0);
+ }
+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true);
}
- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true);
+ // Leaves end - moved down when async processing
} else {
- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ // Leaves start - this is only implemented for per-player mob spawning so this makes everything work if this setting is disabled (actually not use)
+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ spawnCountsReady.set(true);
+ // Leaves end - this is only implemented for per-player mob spawning so this makes everything work if this setting is disabled (actually not use)
}
// Paper end
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
- this.lastSpawnState = spawnercreature_d;
+ // this.lastSpawnState = spawnercreature_d; // Leaves - asynchronously
gameprofilerfiller.popPush("filteringLoadedChunks");
// Paper - moved down
this.level.timings.chunkTicks.startTiming(); // Paper
@@ -761,8 +773,8 @@ public class ServerChunkCache extends ChunkSource {
if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking
chunk1.incrementInhabitedTime(j);
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
+ if (flag2 && (!top.leavesmc.leaves.LeavesConfig.asyncMobSpawning || spawnCountsReady.get()) && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration // Leaves - optimize mob spawning
+ NaturalSpawner.spawnForChunk(this.level, chunk1, lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1); // Leaves - optimize mob spawning
}
if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
@@ -823,6 +835,30 @@ public class ServerChunkCache extends ChunkSource {
}
}
// Paper end - controlled flush for entity tracker packets
+
+ // Leaves start - optimize mob spawning
+ if (top.leavesmc.leaves.LeavesConfig.asyncMobSpawning) {
+ for (ServerPlayer player : this.level.players) {
+ Arrays.fill(player.mobCounts, 0);
+ }
+ if (firstRunSpawnCounts) {
+ firstRunSpawnCounts = false;
+ spawnCountsReady.set(true);
+ }
+ if (chunkMap.playerMobDistanceMap != null && spawnCountsReady.getAndSet(false)) {
+ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
+ int mapped = distanceManager.getNaturalSpawnChunkCount();
+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator =
+ level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
+ top.leavesmc.leaves.util.IterableWrapper<Entity> wrappedIterator =
+ new top.leavesmc.leaves.util.IterableWrapper<Entity>(objectiterator);
+ lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), true); // try add LocalMobCapCalculator
+ objectiterator.finishedIterating();
+ spawnCountsReady.set(true);
+ });
+ }
+ }
+ // Leaves end - optimize mob spawning
}
}
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 15d266fc97eb73338f4f6fb2cfe25d6861e79810..70773c525bcf33d4d4764ce4a16dedddc423b02b 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -173,7 +173,7 @@ public final class NaturalSpawner {
}
difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
}
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) {
+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0 && (!world.paperConfig().entities.spawning.perPlayerMobSpawns || info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit))) { // Leaves - be vanilla
// Paper end
// CraftBukkit end
Objects.requireNonNull(info);
diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
index 4cdfc433df67afcd455422e9baf56f167dd712ae..a6e0f5dab21d806e0c7744b2a337cded2739d870 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
@@ -8,7 +8,7 @@ import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
public class EntityTickList {
- private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking?
+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Leaves - private -> public
private void ensureActiveIsNotIterated() {
// Paper - replace with better logic, do not delay removals
diff --git a/src/main/java/top/leavesmc/leaves/LeavesConfig.java b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
index 937e84214223e978b3b6bc6107974c61e2d0f558..170f1f964e5cd3d1f1590106e31718dced04d0ef 100644
--- a/src/main/java/top/leavesmc/leaves/LeavesConfig.java
+++ b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
@@ -226,7 +226,16 @@ public final class LeavesConfig {
}
noChatSign = getBoolean("settings.misc.no-chat-sign", noChatSign);
}
-
+
+ public static boolean asyncMobSpawning = false;
+ private static boolean asyncMobSpawningLock = false;
+ private static void asyncMobSpawning() {
+ if (!asyncMobSpawningLock) {
+ asyncMobSpawning = getBoolean("settings.performance.async-mob-spawning", asyncMobSpawning);
+ asyncMobSpawningLock = true;
+ }
+ }
+
public static final class WorldConfig {
public final String worldName;