mirror of
https://github.com/BX-Team/DivineMC.git
synced 2025-12-19 14:59:25 +00:00
204 lines
12 KiB
Diff
204 lines
12 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 51b79f614417f231951e9ba05b29ff0044e081e7..dee93ae262a2a06e68dfe8ae1b7193172c436990 100644
|
|
--- a/net/minecraft/server/MinecraftServer.java
|
|
+++ b/net/minecraft/server/MinecraftServer.java
|
|
@@ -291,6 +291,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // DivineMC - Parallel world ticking
|
|
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) {
|
|
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
|
|
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
|
index aa515bc07b899351f2b0ac8d61df8e5586616084..83157183ab2363eebd8a19d1e018b6bd5c736507 100644
|
|
--- a/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -268,6 +268,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,
|
|
@@ -651,6 +655,35 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
} catch (InterruptedException ex) {
|
|
throw new RuntimeException("Interrupted managed block during region ticking", ex);
|
|
}
|
|
+
|
|
+ // 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 ArrayList<>(level.entityTickList.entities), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), true);
|
|
+ } finally { }
|
|
+ spawnCountsReady.set(true);
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+ // DivineMC end - Async mob spawning
|
|
}
|
|
// DivineMC end - Regionized Chunk Ticking
|
|
|
|
@@ -696,27 +729,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;
|
|
@@ -730,7 +767,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();
|
|
}
|
|
@@ -738,8 +775,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
for (LevelChunk levelChunk : chunks) {
|
|
ChunkPos pos = levelChunk.getPos();
|
|
levelChunk.incrementInhabitedTime(timeInhabited);
|
|
- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
|
|
- NaturalSpawner.spawnForChunk(this.level, levelChunk, spawnState, filteredSpawningCategories);
|
|
+ // DivineMC start - Async mob spawning
|
|
+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && (!org.bxteam.divinemc.DivineConfig.enableAsyncSpawning || spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
|
|
+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories);
|
|
+ // DivineMC end - Async mob spawning
|
|
}
|
|
|
|
if (true) { // Paper - rewrite chunk system
|
|
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
|
index 1056a17c53e7d16d5fba7f9a354dfbc235d4d974..3e9ab446632ffe56de45f7622db44070e1cbaf1f 100644
|
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -155,7 +155,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;
|
|
@@ -682,6 +693,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 018a04674897cfcec0e8de5cb2ab06243a994ae3..8c1de4654a3a29e75716a03efd476b8a3b7fe9e9 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
|
|
// DivineMC start - Parallel world ticking
|
|
private final net.minecraft.server.level.ServerLevel serverLevel;
|
|
|
|
@@ -43,13 +43,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
|
|
}
|