9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00
Files
DivineMC/divinemc-server/minecraft-patches/features/0040-Async-mob-spawning.patch
2025-04-24 19:47:15 +03:00

216 lines
13 KiB
Diff

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] Async mob spawning
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 0270e0045a76927e156f7bf54fed7ca406a113f8..d19337ce45a8ab7f6438f9101d61d17b70d68a47 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -289,6 +289,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected boolean upnp = false; // Purpur - UPnP Port Forwarding
public final org.bxteam.divinemc.util.tps.TPSCalculator tpsCalculator = new org.bxteam.divinemc.util.tps.TPSCalculator(); // DivineMC - Lag compensation
public final Set<net.minecraft.world.entity.Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // DivineMC - Skip EntityScheduler's executeTick checks if there isn't any tasks to be run
+ public org.bxteam.divinemc.util.AsyncProcessor mobSpawnExecutor = new org.bxteam.divinemc.util.AsyncProcessor("mob_spawning"); // DivineMC - Async mob spawning
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
AtomicReference<S> atomicReference = new AtomicReference<>();
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 9a65321ce62f21b150d29be30dbae7dba0ff40be..3bbaa9bdc7d49b456b5749e4880354c9f793a8b6 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -182,6 +182,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Paper end - chunk tick iteration optimisations
+ // DivineMC start - Async mob spawning
+ public boolean firstRunSpawnCounts = true;
+ public final java.util.concurrent.atomic.AtomicBoolean spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false);
+ // DivineMC end - Async mob spawning
public ServerChunkCache(
ServerLevel level,
@@ -505,6 +509,35 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
this.broadcastChangedChunks();
}
+
+ // DivineMC start - Async mob spawning
+ if (org.bxteam.divinemc.DivineConfig.enableAsyncSpawning) {
+ for (ServerPlayer player : this.level.players) {
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
+ player.mobCounts[ii] = 0;
+
+ int newBackoff = player.mobBackoffCounts[ii] - 1;
+ if (newBackoff < 0) {
+ newBackoff = 0;
+ }
+ player.mobBackoffCounts[ii] = newBackoff;
+ }
+ }
+ if (firstRunSpawnCounts) {
+ firstRunSpawnCounts = false;
+ spawnCountsReady.set(true);
+ }
+ if (spawnCountsReady.getAndSet(false)) {
+ MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
+ int mapped = distanceManager.getNaturalSpawnChunkCount();
+ try {
+ lastSpawnState = NaturalSpawner.createState(mapped, new java.util.ArrayList<>(level.entityTickList.entities), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), true);
+ } finally { }
+ spawnCountsReady.set(true);
+ });
+ }
+ }
+ // DivineMC end - Async mob spawning
}
private void broadcastChangedChunks() {
@@ -522,27 +555,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 - Async 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.DivineConfig.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 - Async 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<MobCategory> filteredSpawningCategories;
@@ -556,7 +593,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 - Async mob spawning
} else {
filteredSpawningCategories = List.of();
}
@@ -571,7 +608,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 - Async mob spawning
}
} finally {
list.clear();
@@ -590,11 +627,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 - Async mob spawning
+ if (!spawnCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && (!org.bxteam.divinemc.DivineConfig.enableAsyncSpawning || spawnCountsReady.get())) { // Paper - rewrite chunk system
+ NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
}
+ // DivineMC end - Async mob spawning
}
private void getFullChunk(long chunkPos, Consumer<LevelChunk> fullChunkGetter) {
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 68a074a1eb11b158af773a2c44aa49d5d8462080..a5f6f50ad1e276a908347d9c21527fb583734538 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -149,7 +149,18 @@ public final class NaturalSpawner {
return list;
}
+ private static int maxCapPerPlayer = -1; // DivineMC - Async mob spawning
+
public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List<MobCategory> categories) {
+ // DivineMC start - Async mob spawning
+ if (maxCapPerPlayer < 0) {
+ maxCapPerPlayer = 0;
+ for (final MobCategory value : MobCategory.values()) {
+ maxCapPerPlayer += value.getMaxInstancesPerChunk();
+ }
+ }
+ // DivineMC end - Async mob spawning
+
for (MobCategory mobCategory : categories) {
// Paper start - Optional per player mob spawns
final boolean canSpawn;
@@ -680,6 +691,13 @@ public final class NaturalSpawner {
}
boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
+ // DivineMC start - Async mob spawning
+ if (this.localMobCapCalculator == null) {
+ LOGGER.warn("Local mob cap calculator was null! Report to DivineMC!");
+ return false;
+ }
+ // DivineMC end - Async mob spawning
+
return this.localMobCapCalculator.canSpawn(category, chunkPos);
}
}
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index c89701d7bdc9b889038d3c52f2232fb17624b113..9e75320e51886e0f93c23683d8614128f44a613e 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 {
- 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
+ public final java.util.concurrent.ConcurrentLinkedQueue<Entity> entities = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - rewrite chunk system // DivineMC - Async mob spawning
private void ensureActiveIsNotIterated() {
// Paper - rewrite chunk system
@@ -33,13 +33,13 @@ public class EntityTickList {
// Paper start - rewrite chunk system
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
// (by dfl iterator() is configured to not iterate over new entries)
- final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
+ final java.util.Iterator<Entity> iterator = this.entities.iterator(); // DivineMC - Async mob spawning
try {
while (iterator.hasNext()) {
entity.accept(iterator.next());
}
} finally {
- iterator.finishedIterating();
+ //iterator.finishedIterating(); // DivineMC - Async mob spawning
}
// Paper end - rewrite chunk system
}