9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0176-optimize-mob-spawning.patch
Taiyou cc98f4982e 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>
2025-08-27 20:00:46 +02:00

445 lines
27 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <hayanesuru@outlook.jp>
Date: Tue, 3 Jun 2025 15:20:59 +0900
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
index 52a2b993bbd1ad4851b3273af6ecbc069beb5b84..f66f16332bd1af89a44b71bc015d52a2aeda09de 100644
--- a/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
private final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceOpenHashSet<>();
@Nullable
@VisibleForDebug
- private NaturalSpawner.SpawnState lastSpawnState;
+ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf - optimize mob spawning
+ private long delayTimeInhabited = 0L; // Leaf - optimize mob spawning
+ private long delaySpawn = -1L; // Leaf - optimize mob spawning
// Paper start
public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
public int getFullChunksCount() {
@@ -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();
}
- for (LevelChunk levelChunk : chunks) {
- ChunkPos pos = levelChunk.getPos();
- levelChunk.incrementInhabitedTime(timeInhabited);
- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && lastSpawnState != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot // Pufferfish // Leaf - Don't spawn if lastSpawnState is null
- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish
+ // Leaf start - optimize mob spawning
+ var lastSpawnState1 = this.lastSpawnState;
+ 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 time = level.getGameTime();
+ for (LevelChunk levelChunk : chunks) {
+ ChunkPos pos = levelChunk.getPos();
+ levelChunk.incrementInhabitedTime(sumTimeInhabited);
+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish
+ }
}
+ if (delaySpawn != -1L) {
+ time = delaySpawn;
+ for (LevelChunk levelChunk : chunks) {
+ ChunkPos pos = levelChunk.getPos();
+ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot
+ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish
+ }
+ }
+ }
+ delaySpawn = -1L;
+ delayTimeInhabited = 0L;
+ } else {
+ // unlikely
+ delayTimeInhabited += timeInhabited;
+ delaySpawn = level.getGameTime();
+ }
+ // Leaf end - optimize mob spawning
+ for (LevelChunk levelChunk : chunks) { // Leaf - optimize mob spawning - split to 2 loop
if (true) { // Paper - rewrite chunk system
this.level.tickChunk(levelChunk, _int);
}
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 9b37b763c6555705f3e256010f508b5a0c2cdb66..418fd9db5a06af5369da597762c0757453929f83 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -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;
}
+ @Deprecated(forRemoval = true) // Leaf - optimize mob spawning
public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List<MobCategory> categories) {
+ // Leaf start - optimize mob spawning
+ spawnForChunk(level, chunk, spawnState, categories, level.getGameTime());
+ }
+ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List<MobCategory> categories, long gameTime) {
+ // Leaf end - optimize mob spawning
for (MobCategory mobCategory : categories) {
// Paper start - Optional per player mob spawns
final boolean canSpawn;
@@ -174,7 +237,7 @@ public final class NaturalSpawner {
}
// Paper end - throttle failed spawn attempts
if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
- spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts
+ spawnThisTick = ticksPerSpawnTmp != 0 && gameTime % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts // Leaf - optimize mob spawning
limit = level.getWorld().getSpawnLimit(spawnCategory);
}
@@ -238,12 +301,14 @@ public final class NaturalSpawner {
// Paper end - throttle failed spawn attempts
) {
// Paper end - Optional per player mob spawns
- BlockPos randomPosWithin = getRandomPosWithin(level, chunk);
- if (randomPosWithin.getY() >= level.getMinY() + 1) {
- return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts
+ // Leaf start - optimize mob spawning
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
+ mutableRandomPosWithin(pos, level, chunk);
+ if (pos.getY() < level.getMinY() + 1) {
+ return 0;
}
-
- return 0; // Paper - throttle failed spawn attempts
+ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts
+ // Leaf end - optimize mob spawning
}
@VisibleForDebug
@@ -275,31 +340,59 @@ public final class NaturalSpawner {
StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY();
+ int posX = pos.getX(); // Leaf - optimize mob spawning
+ int posZ = pos.getZ(); // Leaf - optimize mob spawning
int i = 0; // Paper - throttle failed spawn attempts
BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
+ BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf - optimize mob spawning
//int i = 0; // Paper - throttle failed spawn attempts - move up
+ // Leaf start - optimize mob spawning
+ long rand = level.random.nextLong();
+ int bits = 0;
for (int i1 = 0; i1 < 3; i1++) {
- int x = pos.getX();
- int z = pos.getZ();
- int i2 = 6;
+ int x = posX;
+ int z = posZ;
MobSpawnSettings.SpawnerData spawnerData = null;
SpawnGroupData spawnGroupData = null;
- int ceil = Mth.ceil(level.random.nextFloat() * 4.0F);
+ int ceil = (int) ((rand & 0x3L) + 1L);
+ bits += 2;
+ if (bits >= 62) {
+ rand = level.random.nextLong();
+ bits = 0;
+ }
int i3 = 0;
for (int i4 = 0; i4 < ceil; i4++) {
- x += level.random.nextInt(6) - level.random.nextInt(6);
- z += level.random.nextInt(6) - level.random.nextInt(6);
+ int rand1=0,rand2=0,rand3=0,rand4=0,valuesNeeded=4;
+ while (valuesNeeded > 0) {
+ int threeBits = (int) ((rand >>> bits) & 0x7L);
+ bits += 3;
+ if (threeBits != 7 && threeBits != 6) {
+ switch (valuesNeeded) {
+ case 1 -> rand4 = threeBits;
+ case 2 -> rand3 = threeBits;
+ case 3 -> rand2 = threeBits;
+ case 4 -> rand1 = threeBits;
+ }
+ valuesNeeded--;
+ }
+ if (bits >= 62) {
+ rand = level.random.nextLong();
+ bits = 0;
+ }
+ }
+ x += rand1 - rand2;
+ z += rand3 - rand4;
+ // Leaf end - optimize mob spawning
mutableBlockPos.set(x, y, z);
double d = x + 0.5;
double d1 = z + 0.5;
Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, level.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur - mob spawning option to ignore creative players
if (nearestPlayer != null) {
double d2 = nearestPlayer.distanceToSqr(d, y, d1);
- if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn
+ if (level.getWorldBorder().isWithinBounds(mutableBlockPos) && (chunk.getPos().longKey == ChunkPos.asLong(mutableBlockPos) || level.getChunkIfLoadedImmediately(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4) != null) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn // Leaf - optimize mob spawning
if (spawnerData == null) {
Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt(
level, structureManager, generator, category, level.random, mutableBlockPos
@@ -368,8 +461,8 @@ public final class NaturalSpawner {
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) {
return !(distance <= 576.0)
- && !level.getSharedSpawnPos().closerToCenterThan(new Vec3(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5), 24.0)
- && (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos));
+ && !(level.getSharedSpawnPos().distToCenterSqr(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5) < 576.0) // Leaf - optimize mob spawning
+ && (ChunkPos.asLong(pos) == chunk.getPos().longKey || level.isNaturalSpawningAllowed(pos)); // Leaf - optimize mob spawning
}
// Paper start - PreCreatureSpawnEvent
@@ -474,6 +567,17 @@ public final class NaturalSpawner {
}
}
+ // Leaf start - optimize mob spawning
+ private static void mutableRandomPosWithin(BlockPos.MutableBlockPos pos1, Level level, LevelChunk chunk) {
+ ChunkPos pos = chunk.getPos();
+ int randomX = pos.getMinBlockX() + level.random.nextInt(16);
+ int randomZ = pos.getMinBlockZ() + level.random.nextInt(16);
+ int surfaceY = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, randomX, randomZ) + 1;
+ int randomY = Mth.randomBetweenInclusive(level.random, level.getMinY(), surfaceY);
+ pos1.set(randomX, randomY, randomZ);
+ }
+ // Leaf end - optimize mob spawning
+
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
ChunkPos pos = chunk.getPos();
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