9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00

backport 1.21.8 async mob spawn (#479)

* backport: 1.21.8 async mob spawn

* Move into patch

* fix

* fix build

---------

Co-authored-by: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Co-authored-by: hayanesuru <hayanesuru@outlook.jp>
This commit is contained in:
Taiyou
2025-08-27 20:00:46 +02:00
committed by GitHub
parent 3ae338d9b4
commit cc98f4982e
47 changed files with 256 additions and 210 deletions

View File

@@ -4,8 +4,20 @@ Date: Tue, 3 Jun 2025 15:20:59 +0900
Subject: [PATCH] optimize mob spawning Subject: [PATCH] optimize mob spawning
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index f3be481a92b4f5403809c38d3b3431f4096d7a2e..2a5a2b6c28c61fe5cc3c89676472b9695fd3f188 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -280,6 +280,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper end - per player mob count backoff
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
+ // Leaf - diff - async mob spawning - optimize mob spawning
return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
}
// Paper end - Optional per player mob spawns
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3fd734b73 100644 index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..f66f16332bd1af89a44b71bc015d52a2aeda09de 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -19,7 +31,86 @@ index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3
// Paper start // Paper start
public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
public int getFullChunksCount() { public int getFullChunksCount() {
@@ -658,13 +660,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -496,6 +498,23 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
long gameTime = this.level.getGameTime();
long l = gameTime - this.lastInhabitedUpdate;
this.lastInhabitedUpdate = gameTime;
+ // Leaf start - optimize mob spawning
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled && level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ 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
+ }
+ }
+ // Leaf end - optimize mob spawning
if (!this.level.isDebug()) {
this.level.resetIceAndSnowTick(); // Gale - Airplane - optimize random calls in chunk ticking - reset ice & snow tick random
if (this.level.tickRateManager().runsNormally()) {
@@ -518,8 +537,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Pufferfish start - optimize mob spawning
- if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) {
- for (ServerPlayer player : this.level.players) {
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled && level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ /*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;
@@ -531,34 +550,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
player.mobBackoffCounts[ii] = newBackoff;
}
// Paper end - per player mob spawning backoff
- }
+ }*/
if (firstRunSpawnCounts) {
firstRunSpawnCounts = false;
_pufferfish_spawnCountsReady.set(true);
}
if (_pufferfish_spawnCountsReady.getAndSet(false)) {
+ final int mapped = distanceManager.getNaturalSpawnChunkCount();
+ final Iterable<Entity> entities = this.level.getAllEntities();
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();
- }
+ lastSpawnState = NaturalSpawner.createState1(
+ mapped,
+ entities,
+ this.level
+ );
+ // Leaf end - optimize mob spawning
_pufferfish_spawnCountsReady.set(true);
});
}
@@ -658,13 +664,38 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
filteredSpawningCategories = List.of(); filteredSpawningCategories = List.of();
} }
@@ -30,7 +121,8 @@ index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3
- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish - NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish
+ // Leaf start - optimize mob spawning + // Leaf start - optimize mob spawning
+ var lastSpawnState1 = this.lastSpawnState; + var lastSpawnState1 = this.lastSpawnState;
+ if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { + if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || !level.paperConfig().entities.spawning.perPlayerMobSpawns || _pufferfish_spawnCountsReady.get())) {
+ lastSpawnState1.applyPerPlayerMobCount(level); // Leaf - optimize mob spawning
+ long sumTimeInhabited = timeInhabited + delayTimeInhabited; + long sumTimeInhabited = timeInhabited + delayTimeInhabited;
+ long time = level.getGameTime(); + long time = level.getGameTime();
+ for (LevelChunk levelChunk : chunks) { + for (LevelChunk levelChunk : chunks) {
@@ -39,7 +131,7 @@ index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3
+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot + if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish + NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish
+ } + }
+ } }
+ if (delaySpawn != -1L) { + if (delaySpawn != -1L) {
+ time = delaySpawn; + time = delaySpawn;
+ for (LevelChunk levelChunk : chunks) { + for (LevelChunk levelChunk : chunks) {
@@ -48,7 +140,7 @@ index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3
+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish + NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish
+ } + }
+ } + }
} + }
+ delaySpawn = -1L; + delaySpawn = -1L;
+ delayTimeInhabited = 0L; + delayTimeInhabited = 0L;
+ } else { + } else {
@@ -63,10 +155,85 @@ index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..05df09d5f586c90f3169ddf1fa73a1d3
this.level.tickChunk(levelChunk, _int); this.level.tickChunk(levelChunk, _int);
} }
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854194dd665 100644 index 9b37b763c6555705f3e256010f508b5a0c2cdb66..418fd9db5a06af5369da597762c0757453929f83 100644
--- a/net/minecraft/world/level/NaturalSpawner.java --- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -155,7 +155,13 @@ public final class NaturalSpawner { @@ -74,6 +74,7 @@ public final class NaturalSpawner {
return createState(spawnableChunkCount, entities, chunkGetter, calculator, false);
}
+ @Deprecated // Leaf - optimize mob spawning
public static NaturalSpawner.SpawnState createState(
int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
) {
@@ -114,9 +115,65 @@ public final class NaturalSpawner {
}
}
- return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator);
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Leaf - optimize mob spawning
}
+ // Leaf start - optimize mob spawning
+ public static NaturalSpawner.SpawnState createState1(
+ int spawnableChunkCount, Iterable<Entity> entities, ServerLevel level
+ ) {
+ // Paper end - Optional per player mob spawns
+ PotentialCalculator potentialCalculator = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
+ it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> chunkCap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
+ boolean countAllMobsForSpawning = level.paperConfig().entities.spawning.countAllMobsForSpawning;
+ for (Entity entity : entities) {
+ if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) {
+ MobCategory category = entity.getType().getCategory();
+ if (category != MobCategory.MISC) {
+ // Paper start - Only count natural spawns
+ if (!countAllMobsForSpawning &&
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
+ continue;
+ }
+ // Paper end - Only count natural spawns
+ BlockPos blockPos = entity.blockPosition();
+ LevelChunk chunk = level.getChunkSource().getChunkNow(blockPos.getX() >> 4, blockPos.getZ() >> 4);
+ if (chunk != null) {
+ MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType());
+ if (mobSpawnCost != null) {
+ potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
+ }
+
+ map.addTo(category, 1);
+ // Paper start - Optional per player mob spawns
+ final int index = entity.getType().getCategory().ordinal();
+ ++chunkCap.computeIfAbsent(chunk.getPos().toLong(), k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
+ /*
+ final int index = entity.getType().getCategory().ordinal();
+ final var inRange = level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) {
+ continue;
+ }
+
+ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ final net.minecraft.server.level.ServerPlayer player = backingSet[i];
+ if (player == null) continue;
+ ++playerCap.computeIfAbsent(player, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
+ }
+ */
+ // Paper end - Optional per player mob spawns
+ }
+ }
+ }
+ }
+
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, null, chunkCap);
+ }
+ // Leaf end - optimize mob spawning
+
static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
}
@@ -155,7 +212,13 @@ public final class NaturalSpawner {
return list; return list;
} }
@@ -80,7 +247,7 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
for (MobCategory mobCategory : categories) { for (MobCategory mobCategory : categories) {
// Paper start - Optional per player mob spawns // Paper start - Optional per player mob spawns
final boolean canSpawn; final boolean canSpawn;
@@ -174,7 +180,7 @@ public final class NaturalSpawner { @@ -174,7 +237,7 @@ public final class NaturalSpawner {
} }
// Paper end - throttle failed spawn attempts // Paper end - throttle failed spawn attempts
if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
@@ -89,7 +256,7 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
limit = level.getWorld().getSpawnLimit(spawnCategory); limit = level.getWorld().getSpawnLimit(spawnCategory);
} }
@@ -238,12 +244,14 @@ public final class NaturalSpawner { @@ -238,12 +301,14 @@ public final class NaturalSpawner {
// Paper end - throttle failed spawn attempts // Paper end - throttle failed spawn attempts
) { ) {
// Paper end - Optional per player mob spawns // Paper end - Optional per player mob spawns
@@ -109,7 +276,7 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
} }
@VisibleForDebug @VisibleForDebug
@@ -275,31 +283,59 @@ public final class NaturalSpawner { @@ -275,31 +340,59 @@ public final class NaturalSpawner {
StructureManager structureManager = level.structureManager(); StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator(); ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY(); int y = pos.getY();
@@ -177,7 +344,7 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
if (spawnerData == null) { if (spawnerData == null) {
Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt( Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt(
level, structureManager, generator, category, level.random, mutableBlockPos level, structureManager, generator, category, level.random, mutableBlockPos
@@ -368,8 +404,8 @@ public final class NaturalSpawner { @@ -368,8 +461,8 @@ public final class NaturalSpawner {
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) {
return !(distance <= 576.0) return !(distance <= 576.0)
@@ -188,7 +355,7 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
} }
// Paper start - PreCreatureSpawnEvent // Paper start - PreCreatureSpawnEvent
@@ -474,6 +510,17 @@ public final class NaturalSpawner { @@ -474,6 +567,17 @@ public final class NaturalSpawner {
} }
} }
@@ -206,3 +373,72 @@ index 9b37b763c6555705f3e256010f508b5a0c2cdb66..8f999b425bce52352a04ee3ca11b6854
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) { private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
ChunkPos pos = chunk.getPos(); ChunkPos pos = chunk.getPos();
int i = pos.getMinBlockX() + level.random.nextInt(16); int i = pos.getMinBlockX() + level.random.nextInt(16);
@@ -614,18 +718,21 @@ public final class NaturalSpawner {
@Nullable
private EntityType<?> lastCheckedType;
private double lastCharge;
+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> chunkCap; // Leaf - optimize mob spawning
SpawnState(
int spawnableChunkCount,
Object2IntOpenHashMap<MobCategory> mobCategoryCounts,
PotentialCalculator spawnPotential,
- LocalMobCapCalculator localMobCapCalculator
+ LocalMobCapCalculator localMobCapCalculator,
+ it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap // Leaf - optimize mob spawning
) {
this.spawnableChunkCount = spawnableChunkCount;
this.mobCategoryCounts = mobCategoryCounts;
this.spawnPotential = spawnPotential;
this.localMobCapCalculator = localMobCapCalculator;
this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
+ this.chunkCap = playerCap; // Leaf - optimize mob spawning
}
private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) {
@@ -682,5 +789,32 @@ public final class NaturalSpawner {
boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
return this.localMobCapCalculator.canSpawn(category, chunkPos);
}
+
+ // Leaf start - optimize mob spawning
+ public void applyPerPlayerMobCount(ServerLevel level) {
+ if (chunkCap.isEmpty()) {
+ return;
+ }
+ final var iterator = chunkCap.long2ObjectEntrySet().fastIterator();
+ final ca.spottedleaf.moonrise.common.misc.NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
+ while (iterator.hasNext()) {
+ var entry = iterator.next();
+ long chunk = entry.getLongKey();
+ int[] cap = entry.getValue();
+ ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> players = nearbyPlayers.getPlayersByChunk(net.minecraft.world.level.ChunkPos.getX(chunk), net.minecraft.world.level.ChunkPos.getZ(chunk), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (players == null) {
+ continue;
+ }
+ int playersSize = players.size();
+ net.minecraft.server.level.ServerPlayer[] playersRawDataUnchecked = players.getRawDataUnchecked();
+ for (int i = 0; i < playersSize; i++) {
+ int[] p = playersRawDataUnchecked[i].mobCounts;
+ for (int j = 0; j < net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; j++) {
+ p[j] += cap[j];
+ }
+ }
+ }
+ }
+ // Leaf end - optimize mob spawning
}
}
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index 5a90b3bffeeb08a168b370e49d18c5f8b257a980..ba173bc1751c495e6fa497566b5ed3c7a9547364 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<>(org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled); // Paper - rewrite chunk system // Pufferfish - private->public and do thread check
+ 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
// Leaf start - SparklyPaper - parallel world ticking mod
// preserve original constructor

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] throttle mob spawning
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 8f999b425bce52352a04ee3ca11b6854194dd665..48ab439560c6ba83d34163cc7402146e69e7c277 100644 index 6e911df433013480c2688c086a7ff1de321001bc..7764fcca95cef9f0aa46683f41d3a6805507f80a 100644
--- a/net/minecraft/world/level/NaturalSpawner.java --- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -166,6 +166,17 @@ public final class NaturalSpawner { @@ -223,6 +223,17 @@ public final class NaturalSpawner {
// Paper start - Optional per player mob spawns // Paper start - Optional per player mob spawns
final boolean canSpawn; final boolean canSpawn;
int maxSpawns = Integer.MAX_VALUE; int maxSpawns = Integer.MAX_VALUE;

View File

@@ -100,10 +100,10 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194
- // Paper end - detailed watchdog information - // Paper end - detailed watchdog information
} }
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 05df09d5f586c90f3169ddf1fa73a1d3fd734b73..fbcff9a0e4aa68375e7ebcc297ba4a7bf089c4f3 100644 index f66f16332bd1af89a44b71bc015d52a2aeda09de..bb200d3f482c7f623a827d255a9633bb278637e9 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -506,9 +506,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -523,9 +523,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
try { try {
this.collectTickingChunks(list); this.collectTickingChunks(list);
// Paper start - chunk tick iteration optimisation // Paper start - chunk tick iteration optimisation

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] optimize random tick
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index fbcff9a0e4aa68375e7ebcc297ba4a7bf089c4f3..e07fdd22e08cb4e30cf606c055e85b5946e8c046 100644 index bb200d3f482c7f623a827d255a9633bb278637e9..7531e684e16bc038811fbf133caced9c7f5efe07 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -696,6 +696,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -701,6 +701,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
this.level.tickChunk(levelChunk, _int); this.level.tickChunk(levelChunk, _int);
} }
} }

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] count all chunks for ticking
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index e07fdd22e08cb4e30cf606c055e85b5946e8c046..432c98b582ab40f893835a7a24ef9bbdacc49bd7 100644 index 7531e684e16bc038811fbf133caced9c7f5efe07..10fb9cc994ddc8820bed23d722b49b9f33e5fc09 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -589,6 +589,14 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -593,6 +593,14 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
final int size = tickingChunks.size(); final int size = tickingChunks.size();
final ChunkMap chunkMap = this.chunkMap; final ChunkMap chunkMap = this.chunkMap;

View File

@@ -1,190 +0,0 @@
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>
Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.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/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
index c21e00812f1aaa1279834a0562d360d6b89e146c..bc06820e9e68e0ad2f8e4cb1a73d04aedf3f3e40 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
@@ -10,7 +10,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
public static final int ITERATOR_FLAG_SEE_ADDITIONS = 1 << 0;
private final Reference2IntLinkedOpenHashMap<E> indexMap;
- private int firstInvalidIndex = -1;
+ private final java.util.concurrent.atomic.AtomicInteger firstInvalidIndex = new java.util.concurrent.atomic.AtomicInteger(-1); // Leaf - Pufferfish - Async mob spawning - atomic
/* list impl */
private E[] listElements;
@@ -18,18 +18,32 @@ public final class IteratorSafeOrderedReferenceSet<E> {
private final double maxFragFactor;
- private int iteratorCount;
+ private int iteratorCount; // Pufferfish - async mob spawning
+
+ // Pufferfish start - async mob spawning
+ private final boolean threadRestricted;
public IteratorSafeOrderedReferenceSet() {
- this(16, 0.75f, 16, 0.2);
+ this(16, 0.75f, 16, 0.2, false);
+ }
+
+ public IteratorSafeOrderedReferenceSet(final boolean threadRestricted) {
+ this(16, 0.75f, 16, 0.2, threadRestricted);
}
public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity,
final double maxFragFactor) {
+ this(setCapacity, setLoadFactor, arrayCapacity, maxFragFactor, false);
+ }
+
+ public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity,
+ final double maxFragFactor, final boolean threadRestricted) {
this.indexMap = new Reference2IntLinkedOpenHashMap<>(setCapacity, setLoadFactor);
this.indexMap.defaultReturnValue(-1);
this.maxFragFactor = maxFragFactor;
this.listElements = (E[])new Object[arrayCapacity];
+ this.threadRestricted = threadRestricted;
+ // Pufferfish end - async mob spawning
}
/*
@@ -74,16 +88,24 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
*/
+ // Pufferfish start - async mob spawning
+ // Bring back allowSafeIteration from Paper 1.20.4
+ // To defrag only on the main thread
+ private boolean allowSafeIteration() {
+ return !this.threadRestricted || ca.spottedleaf.moonrise.common.util.TickThread.isTickThread();
+ }
+ // Pufferfish end - async mob spawning
+
private double getFragFactor() {
return 1.0 - ((double)this.indexMap.size() / (double)this.listSize);
}
public int createRawIterator() {
- ++this.iteratorCount;
+ if (this.allowSafeIteration()) this.iteratorCount++; // Pufferfish - async mob spawning
if (this.indexMap.isEmpty()) {
return -1;
} else {
- return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0;
+ return this.firstInvalidIndex.get() == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0; // Leaf - Pufferfish - Async mob spawning
}
}
@@ -100,7 +122,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
public void finishRawIterator() {
- if (--this.iteratorCount == 0) {
+ if (--this.iteratorCount == 0) { // Pufferfish - async mob spawning
if (this.getFragFactor() >= this.maxFragFactor) {
this.defrag();
}
@@ -110,14 +132,17 @@ public final class IteratorSafeOrderedReferenceSet<E> {
public boolean remove(final E element) {
final int index = this.indexMap.removeInt(element);
if (index >= 0) {
- if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) {
- this.firstInvalidIndex = index;
+ // Leaf start - Pufferfish - Async mob spawning
+ int firstInvalidIndex = this.firstInvalidIndex.get();
+ if (firstInvalidIndex < 0 || index < firstInvalidIndex) {
+ this.firstInvalidIndex.set(index);
}
+ // Leaf end - Pufferfish - Async mob spawning
if (this.listElements[index] != element) {
throw new IllegalStateException();
}
this.listElements[index] = null;
- if (this.iteratorCount == 0 && this.getFragFactor() >= this.maxFragFactor) {
+ if (this.allowSafeIteration() && this.iteratorCount == 0 && this.getFragFactor() >= this.maxFragFactor) { // Pufferfish - async mob spawning
this.defrag();
}
//this.check();
@@ -149,14 +174,17 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
private void defrag() {
- if (this.firstInvalidIndex < 0) {
+ // Leaf start - Pufferfish - Async mob spawning
+ int firstInvalidIndex = this.firstInvalidIndex.get();
+ if (firstInvalidIndex < 0) {
return; // nothing to do
}
+ // Leaf end - Pufferfish - Async mob spawning
if (this.indexMap.isEmpty()) {
Arrays.fill(this.listElements, 0, this.listSize, null);
this.listSize = 0;
- this.firstInvalidIndex = -1;
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
//this.check();
return;
}
@@ -166,11 +194,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
int lastValidIndex;
java.util.Iterator<Reference2IntMap.Entry<E>> iterator;
- if (this.firstInvalidIndex == 0) {
+ if (firstInvalidIndex == 0) { // Leaf - Pufferfish - Async mob spawning
iterator = this.indexMap.reference2IntEntrySet().fastIterator();
lastValidIndex = 0;
} else {
- lastValidIndex = this.firstInvalidIndex;
+ lastValidIndex = firstInvalidIndex; // Leaf - Pufferfish - Async mob spawning
final E key = backingArray[lastValidIndex - 1];
iterator = this.indexMap.reference2IntEntrySet().fastIterator(new Reference2IntMap.Entry<E>() {
@Override
@@ -201,7 +229,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
// cleanup end
Arrays.fill(backingArray, lastValidIndex, this.listSize, null);
this.listSize = lastValidIndex;
- this.firstInvalidIndex = -1;
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
//this.check();
}
@@ -219,7 +247,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
- ++this.iteratorCount;
+ if (this.allowSafeIteration()) ++this.iteratorCount; // Pufferfish - async mob spawning
return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
}
@@ -306,7 +334,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
this.lastReturned = null;
this.finished = true;
- this.set.finishRawIterator();
+ if (this.set.allowSafeIteration()) this.set.finishRawIterator(); // Pufferfish - async mob spawning - diff on change
}
}
}