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

fix async mob spawning data race

This commit is contained in:
hayanesuru
2025-06-26 18:50:24 +09:00
parent 1229df1952
commit 333373d204
3 changed files with 58 additions and 32 deletions

View File

@@ -17,7 +17,7 @@ index 79674f4bd7a12c42dec19a4175012d7a2dc88b84..9f4ba5b7e054bbe70a820068f22fe8a6
}
// Paper end - Optional per player mob spawns
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818abb2551c 100644
index 46e171ca454253c32e22c0c18587e9a7ba19f331..51b4a043c09eef84bf926ea02bf588e9ce900788 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -70,11 +70,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -47,7 +47,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818
mobCapCalculator, // This is the key fix - was previously null
level.paperConfig().entities.spawning.perPlayerMobSpawns
);
@@ -627,9 +627,32 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -627,9 +627,23 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
// Paper end - chunk tick iteration optimisation
@@ -56,16 +56,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818
+ // Leaf start
+ var lastSpawnState1 = lastSpawnState;
+ if ((!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && lastSpawnState1 != null) {
+ if (!lastSpawnState1.playerCap.isEmpty()) {
+ for (final var iterator = lastSpawnState1.playerCap.reference2ReferenceEntrySet().fastIterator(); iterator.hasNext(); ) {
+ final var entry = iterator.next();
+ final var entryKey = entry.getKey().mobCounts;
+ final var entryVal = entry.getValue();
+ for (int i = 0; i < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; i++) {
+ entryKey[i] += entryVal[i];
+ }
+ }
+ }
+ lastSpawnState1.applyPerPlayerMobCount(level);
+ if (list instanceof it.unimi.dsi.fastutil.objects.ReferenceArrayList<LevelChunk> arrayList) {
+ LevelChunk[] levelChunks = arrayList.elements();
+ int size = arrayList.size();
@@ -83,7 +74,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818
list.clear();
}
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f7bebabf0 100644
index f242941ce06d356a025e306efe78c688e9b755c4..ee38a26fd1d9b8d8718a16af4fe019645606a956 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -1,5 +1,7 @@
@@ -102,12 +93,12 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
public static NaturalSpawner.SpawnState createState(
int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
) {
@@ -108,7 +111,61 @@ public final class NaturalSpawner {
@@ -108,7 +111,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.objects.Reference2ReferenceOpenHashMap<>());
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Leaf
+ }
+
+ public static NaturalSpawner.SpawnState createState1(
@@ -116,7 +107,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ // Paper end - Optional per player mob spawns
+ PotentialCalculator potentialCalculator = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
+ it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<net.minecraft.server.level.ServerPlayer, int[]> playerCap = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>();
+ 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()))) {
@@ -144,6 +135,9 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ // Paper start - Optional per player mob spawns
+ if (countMobs) {
+ final int index = entity.getType().getCategory().ordinal();
+ ++chunkCap.computeIfAbsent(chunk.getPos().longKey, 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;
@@ -155,17 +149,18 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ 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, calculator, playerCap);
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, chunkCap);
}
static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
@@ -265,28 +322,67 @@ public final class NaturalSpawner {
@@ -265,28 +326,67 @@ public final class NaturalSpawner {
MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity, final boolean nothing
// Paper end - throttle failed spawn attempts
) {
@@ -241,7 +236,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
mutableBlockPos.set(x, y, z);
double d = x + 0.5;
double d1 = z + 0.5;
@@ -295,8 +391,8 @@ public final class NaturalSpawner {
@@ -295,8 +395,8 @@ public final class NaturalSpawner {
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 (spawnerData == null) {
@@ -252,7 +247,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
);
if (randomSpawnMobAt.isEmpty()) {
break;
@@ -307,7 +403,7 @@ public final class NaturalSpawner {
@@ -307,7 +407,7 @@ public final class NaturalSpawner {
}
// Paper start - PreCreatureSpawnEvent
@@ -261,7 +256,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
// Paper start - per player mob count backoff
if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category);
@@ -414,6 +510,44 @@ public final class NaturalSpawner {
@@ -414,6 +514,44 @@ public final class NaturalSpawner {
&& level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent
}
@@ -306,7 +301,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
@Nullable
private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) {
@@ -449,6 +583,17 @@ public final class NaturalSpawner {
@@ -449,6 +587,17 @@ public final class NaturalSpawner {
: mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
}
@@ -324,7 +319,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
private static boolean canSpawnMobAt(
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos
) {
@@ -463,6 +608,16 @@ public final class NaturalSpawner {
@@ -463,6 +612,16 @@ public final class NaturalSpawner {
: generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos)), structureManager, cetagory, pos); // Leaf - cache getBiome
}
@@ -341,7 +336,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) {
Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS);
@@ -472,6 +627,28 @@ public final class NaturalSpawner {
@@ -472,6 +631,28 @@ public final class NaturalSpawner {
}
}
@@ -370,11 +365,11 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
ChunkPos pos = chunk.getPos();
int i = pos.getMinBlockX() + level.random.nextInt(16);
@@ -612,18 +789,21 @@ public final class NaturalSpawner {
@@ -612,18 +793,21 @@ public final class NaturalSpawner {
@Nullable
private EntityType<?> lastCheckedType;
private double lastCharge;
+ public final it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<net.minecraft.server.level.ServerPlayer, int[]> playerCap; // Leaf
+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap; // Leaf
SpawnState(
int spawnableChunkCount,
@@ -382,17 +377,48 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
PotentialCalculator spawnPotential,
- LocalMobCapCalculator localMobCapCalculator
+ LocalMobCapCalculator localMobCapCalculator,
+ it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<net.minecraft.server.level.ServerPlayer, int[]> playerCap
+ it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap // Leaf
) {
this.spawnableChunkCount = spawnableChunkCount;
this.mobCategoryCounts = mobCategoryCounts;
this.spawnPotential = spawnPotential;
this.localMobCapCalculator = localMobCapCalculator;
this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
+ this.playerCap = playerCap;
+ this.playerCap = playerCap; // Leaf
}
private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) {
@@ -680,5 +864,30 @@ public final class NaturalSpawner {
boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
return this.localMobCapCalculator.canSpawn(category, chunkPos);
}
+
+ // Leaf start
+ public void applyPerPlayerMobCount(ServerLevel level) {
+ if (playerCap.isEmpty()) {
+ return;
+ }
+ final var iterator = playerCap.long2ObjectEntrySet().fastIterator();
+ final var nearbyPlayers = level.moonrise$getNearbyPlayers();
+ while (iterator.hasNext()) {
+ final var entry = iterator.next();
+ final long chunk = entry.getLongKey();
+ final int[] cap = entry.getValue();
+ var players = nearbyPlayers.getPlayersByChunk(ChunkPos.getX(chunk), ChunkPos.getZ(chunk), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (players == null) continue;
+ int playersSize = players.size();
+ var 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
}
}
diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java
index fbe93098ce0366054a6da857cd808af1431b6612..a7cadf708bd79abb96fed0d41b4dbc00b265a9a6 100644
--- a/net/minecraft/world/level/StructureManager.java

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
index 8d50517a4c6c4f14dcb14ea18904749f7bebabf0..896868b34ba5ec76be693aca1c3b75c2afbcc72e 100644
index ee38a26fd1d9b8d8718a16af4fe019645606a956..0d587e160cb1d55fe7bc23f8ab3c3cdb48e98759 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -211,6 +211,17 @@ public final class NaturalSpawner {
@@ -215,6 +215,17 @@ public final class NaturalSpawner {
// Paper start - Optional per player mob spawns
final boolean canSpawn;
int maxSpawns = Integer.MAX_VALUE;

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
index fd7f38558a598192703a8d79fc4d3818abb2551c..f2fccba5ce54cfed9ac76555e8d2860a3070fe1c 100644
index 51b4a043c09eef84bf926ea02bf588e9ce900788..8ca23e74011b70a8524fb62d1acd4344c3e71eb8 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -657,7 +657,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -648,7 +648,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
list.clear();
}