diff --git a/patches/server/0005-Paper-config-files.patch b/patches/server/0005-Paper-config-files.patch index aa4e3b385f8e..056902d24dc1 100644 --- a/patches/server/0005-Paper-config-files.patch +++ b/patches/server/0005-Paper-config-files.patch @@ -1403,10 +1403,10 @@ index 0000000000000000000000000000000000000000..990d1bb46e0f9719f4e9af928d80ac6f +} diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java new file mode 100644 -index 0000000000000000000000000000000000000000..7509b17414f836e8b53fc62b02b386ba4e8c5ca9 +index 0000000000000000000000000000000000000000..f9a2bbd0aa64bd3a77f1dc80cbdc5ef52da89b5b --- /dev/null +++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -@@ -0,0 +1,551 @@ +@@ -0,0 +1,560 @@ +package io.papermc.paper.configuration; + +import com.google.common.collect.HashBasedTable; @@ -1595,6 +1595,15 @@ index 0000000000000000000000000000000000000000..7509b17414f836e8b53fc62b02b386ba + @MergeMap + public Reference2IntMap ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); + ++ public SpawningThrottle spawningThrottle; ++ ++ public class SpawningThrottle extends ConfigurationPart { ++ public IntOr.Disabled failedAttemptsThreshold = IntOr.Disabled.DISABLED; ++ ++ @MergeMap ++ public Reference2IntMap throttledTicksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); ++ } ++ + @ConfigSerializable + public record DespawnRange(@Required int soft, @Required int hard) { + } diff --git a/patches/server/1042-Throttle-failed-spawn-attempts.patch b/patches/server/1042-Throttle-failed-spawn-attempts.patch new file mode 100644 index 000000000000..6cd3e7127e81 --- /dev/null +++ b/patches/server/1042-Throttle-failed-spawn-attempts.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: froobynooby +Date: Wed, 17 Jul 2024 18:46:11 +0930 +Subject: [PATCH] Throttle failed spawn attempts + +This patch adds the option to use longer ticks-per-spawn for a given +mob type in chunks where spawn attempts are consecutively failing. + +This behaviour is particularly useful on servers where players build +mob farms. Mob farm designs often require making surrounding chunks +spawnproof, which causes the server to waste CPU cycles trying to spawn mobs in +vain. Throttling spawn attempts in suspected spawnproof chunks improves +performance without noticeably advantaging or disadvantaging the mob farm. + +diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +index ed8032495af9ce9c23419224814b8d27e4a97c17..a5b4fca33df8cfffbe7000f77e4f999b0256edc5 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -140,8 +140,14 @@ public final class NaturalSpawner { + boolean spawnThisTick = true; + int limit = enumcreaturetype.getMaxInstancesPerChunk(); + SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype); ++ // Paper start - throttle failed spawn attempts ++ long ticksPerSpawn = world.ticksPerSpawnCategory.getLong(spawnCategory); ++ if (world.paperConfig().entities.spawning.spawningThrottle.failedAttemptsThreshold.test(threshold -> chunk.failedSpawnAttempts[enumcreaturetype.ordinal()] > threshold)) { ++ ticksPerSpawn = Math.max(ticksPerSpawn, world.paperConfig().entities.spawning.spawningThrottle.throttledTicksPerSpawn.getOrDefault(enumcreaturetype, -1)); ++ } ++ // Paper end - throttle failed spawn attempts + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { +- spawnThisTick = world.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % world.ticksPerSpawnCategory.getLong(spawnCategory) == 0; ++ spawnThisTick = world.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts + limit = world.getWorld().getSpawnLimit(spawnCategory); + } + +@@ -178,6 +184,13 @@ public final class NaturalSpawner { + difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); + info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum); + // Paper end - Optional per player mob spawns ++ // Paper start - throttle failed spawn attempts ++ if (spawnCount == 0) { ++ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()]++; ++ } else { ++ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()] = 0; ++ } ++ // Paper end - throttle failed spawn attempts + } + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +index 75c8125e20b70433fe9d143a3193d821043327c3..fdc5fbb5d713a170741c3e1b7d1980bbf80b5985 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -84,6 +84,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom + public final Map blockEntities = new Object2ObjectOpenHashMap(); + protected final LevelHeightAccessor levelHeightAccessor; + protected final LevelChunkSection[] sections; ++ public final long[] failedSpawnAttempts = new long[net.minecraft.world.entity.MobCategory.values().length]; // Paper - throttle failed spawn attempts + + // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java +index ecf7ba97d397e15e4fcaf4c1e1f48bb7972e60dc..3727bad0fb880f5e8501fea7d85cccf3174def3f 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java +@@ -302,6 +302,16 @@ public class ChunkSerializer { + ((ChunkAccess) object1).addPackedPostProcess(nbttaglist2.getShort(i1), j1); + } + } ++ // Paper start - throttle failed spawn attempts ++ if (nbt.contains("Paper.FailedSpawnAttempts", Tag.TAG_COMPOUND)) { ++ CompoundTag failedSpawnAttemptsTag = nbt.getCompound("Paper.FailedSpawnAttempts"); ++ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) { ++ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName(), Tag.TAG_LONG)) { ++ ((ChunkAccess) object1).failedSpawnAttempts[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLong(mobCategory.getSerializedName()); ++ } ++ } ++ } ++ // Paper end - throttle failed spawn attempts + + ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.loadLightHook(world, chunkPos, nbt, (ChunkAccess)object1); // Paper - rewrite chunk system - note: it's ok to pass the raw value instead of wrapped + +@@ -545,6 +555,18 @@ public class ChunkSerializer { + nbttagcompound.put("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound()); + } + // CraftBukkit end ++ // Paper start - throttle failed spawn attempts ++ CompoundTag failedSpawnAttemptsTag = new CompoundTag(); ++ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) { ++ long failedSpawnAttempts = chunk.failedSpawnAttempts[mobCategory.ordinal()]; ++ if (failedSpawnAttempts > 0) { ++ failedSpawnAttemptsTag.putLong(mobCategory.getSerializedName(), failedSpawnAttempts); ++ } ++ } ++ if (!failedSpawnAttemptsTag.isEmpty()) { ++ nbttagcompound.put("Paper.FailedSpawnAttempts", failedSpawnAttemptsTag); ++ } ++ // Paper end - throttle failed spawn attempts + ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.saveLightHook(world, chunk, nbttagcompound); // Paper - rewrite chunk system + return nbttagcompound; + }