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 // 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 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818abb2551c 100644 index 46e171ca454253c32e22c0c18587e9a7ba19f331..51b4a043c09eef84bf926ea02bf588e9ce900788 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,11 +70,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -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 mobCapCalculator, // This is the key fix - was previously null
level.paperConfig().entities.spawning.perPlayerMobSpawns 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 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 // Paper end - chunk tick iteration optimisation
@@ -56,16 +56,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818
+ // Leaf start + // Leaf start
+ var lastSpawnState1 = lastSpawnState; + var lastSpawnState1 = lastSpawnState;
+ if ((!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && lastSpawnState1 != null) { + if ((!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && lastSpawnState1 != null) {
+ if (!lastSpawnState1.playerCap.isEmpty()) { + lastSpawnState1.applyPerPlayerMobCount(level);
+ 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];
+ }
+ }
+ }
+ if (list instanceof it.unimi.dsi.fastutil.objects.ReferenceArrayList<LevelChunk> arrayList) { + if (list instanceof it.unimi.dsi.fastutil.objects.ReferenceArrayList<LevelChunk> arrayList) {
+ LevelChunk[] levelChunks = arrayList.elements(); + LevelChunk[] levelChunks = arrayList.elements();
+ int size = arrayList.size(); + int size = arrayList.size();
@@ -83,7 +74,7 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..fd7f38558a598192703a8d79fc4d3818
list.clear(); list.clear();
} }
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 f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f7bebabf0 100644 index f242941ce06d356a025e306efe78c688e9b755c4..ee38a26fd1d9b8d8718a16af4fe019645606a956 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
@@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
@@ -102,12 +93,12 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
public static NaturalSpawner.SpawnState createState( public static NaturalSpawner.SpawnState createState(
int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs 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);
+ 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( + public static NaturalSpawner.SpawnState createState1(
@@ -116,7 +107,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ // Paper end - Optional per player mob spawns + // Paper end - Optional per player mob spawns
+ PotentialCalculator potentialCalculator = new PotentialCalculator(); + PotentialCalculator potentialCalculator = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>(); + 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; + boolean countAllMobsForSpawning = level.paperConfig().entities.spawning.countAllMobsForSpawning;
+ for (Entity entity : entities) { + for (Entity entity : entities) {
+ if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) { + if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) {
@@ -144,6 +135,9 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ // Paper start - Optional per player mob spawns + // Paper start - Optional per player mob spawns
+ if (countMobs) { + if (countMobs) {
+ final int index = entity.getType().getCategory().ordinal(); + 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); + final var inRange = level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) { + if (inRange == null) {
+ continue; + continue;
@@ -155,17 +149,18 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
+ if (player == null) continue; + if (player == null) continue;
+ ++playerCap.computeIfAbsent(player, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index]; + ++playerCap.computeIfAbsent(player, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
+ } + }
+ */
+ } + }
+ // Paper end - Optional per player mob spawns + // 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) { 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 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 // Paper end - throttle failed spawn attempts
) { ) {
@@ -241,7 +236,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
mutableBlockPos.set(x, y, z); mutableBlockPos.set(x, y, z);
double d = x + 0.5; double d = x + 0.5;
double d1 = z + 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); 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.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn
if (spawnerData == null) { if (spawnerData == null) {
@@ -252,7 +247,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
); );
if (randomSpawnMobAt.isEmpty()) { if (randomSpawnMobAt.isEmpty()) {
break; break;
@@ -307,7 +403,7 @@ public final class NaturalSpawner { @@ -307,7 +407,7 @@ public final class NaturalSpawner {
} }
// Paper start - PreCreatureSpawnEvent // Paper start - PreCreatureSpawnEvent
@@ -261,7 +256,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
// Paper start - per player mob count backoff // Paper start - per player mob count backoff
if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) { if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category); 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)); && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent
} }
@@ -306,7 +301,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
@Nullable @Nullable
private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) { 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); : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
} }
@@ -324,7 +319,7 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
private static boolean canSpawnMobAt( private static boolean canSpawnMobAt(
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos 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 : 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) { public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) { if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) {
Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS); 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) { 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);
@@ -612,18 +789,21 @@ public final class NaturalSpawner { @@ -612,18 +793,21 @@ public final class NaturalSpawner {
@Nullable @Nullable
private EntityType<?> lastCheckedType; private EntityType<?> lastCheckedType;
private double lastCharge; 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( SpawnState(
int spawnableChunkCount, int spawnableChunkCount,
@@ -382,17 +377,48 @@ index f242941ce06d356a025e306efe78c688e9b755c4..8d50517a4c6c4f14dcb14ea18904749f
PotentialCalculator spawnPotential, PotentialCalculator spawnPotential,
- LocalMobCapCalculator localMobCapCalculator - LocalMobCapCalculator localMobCapCalculator
+ 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.spawnableChunkCount = spawnableChunkCount;
this.mobCategoryCounts = mobCategoryCounts; this.mobCategoryCounts = mobCategoryCounts;
this.spawnPotential = spawnPotential; this.spawnPotential = spawnPotential;
this.localMobCapCalculator = localMobCapCalculator; this.localMobCapCalculator = localMobCapCalculator;
this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts); this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
+ this.playerCap = playerCap; + this.playerCap = playerCap; // Leaf
} }
private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) { 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 diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java
index fbe93098ce0366054a6da857cd808af1431b6612..a7cadf708bd79abb96fed0d41b4dbc00b265a9a6 100644 index fbe93098ce0366054a6da857cd808af1431b6612..a7cadf708bd79abb96fed0d41b4dbc00b265a9a6 100644
--- a/net/minecraft/world/level/StructureManager.java --- 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 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 --- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/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 // 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

@@ -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 fd7f38558a598192703a8d79fc4d3818abb2551c..f2fccba5ce54cfed9ac76555e8d2860a3070fe1c 100644 index 51b4a043c09eef84bf926ea02bf588e9ce900788..8ca23e74011b70a8524fb62d1acd4344c3e71eb8 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
@@ -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(); list.clear();
} }