mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
205 lines
13 KiB
Diff
205 lines
13 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/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
|
index f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c..c1efd558cfbfd2200295ef5755aa496e95deb7d7 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() {
|
|
@@ -655,13 +657,37 @@ 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 || _pufferfish_spawnCountsReady.get())) {
|
|
+ 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..bde7008e14a3b4c0a37a94a4890e2f7fa1ce2466 100644
|
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -155,7 +155,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 +180,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 +244,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 +283,55 @@ 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;
|
|
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) {
|
|
+ if (bits > 61) {
|
|
+ rand = level.random.nextLong();
|
|
+ bits = 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--;
|
|
+ }
|
|
+ }
|
|
+ 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 +400,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 +506,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);
|