9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-21 07:59:26 +00:00
Files
Leaf/patches/server/0097-Paper-PR-Throttle-failed-spawn-attempts.patch
2024-08-23 20:24:06 -04:00

139 lines
8.5 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: froobynooby <froobynooby@froobworld.com>
Date: Wed, 17 Jul 2024 18:46:11 +0930
Subject: [PATCH] Paper PR: Throttle failed spawn attempts
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/11099
For example config in paper-world-defaults.yml
```
spawning-throttle:
failed-attempts-threshold: 1200
throttled-ticks-per-spawn:
ambient: 10 # default value in bukkit.yml tickers-per * 10
axolotls: 10
creature: 4000
monster: 10
underground_water_creature: 10
water_ambient: 10
water_creature: 10
```
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/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
index 95e96fe8aa93efbbb2e0a7dd98377fdc4fe0e6dd..b6728bb0c1974eb236a7e8429b936492ff490bdd 100644
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
@@ -188,6 +188,15 @@ public class WorldConfiguration extends ConfigurationPart {
@MergeMap
public Reference2IntMap<MobCategory> 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<MobCategory> throttledTicksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1)));
+ }
+
@ConfigSerializable
public record DespawnRangePair(@Required DespawnRange hard, @Required DespawnRange soft) {
public static DespawnRangePair createDefault() {
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 7e1a007ed2f2c625f01fea210c9935cb02d119e0..8e9ada03e5134d2f776bbbf872087121f649c19d 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -138,8 +138,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);
}
@@ -176,6 +182,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 5bb8fe022f580a626a99324f53515890a99b798d..5cee82d3d17739858e47f24ec5340233f09d0713 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<BlockPos, BlockEntity> 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
// Leaf start - Matter - Feature Secure Seed
private boolean slimeChunk;
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 4bd048387651250135f963303c78c17f8473cfee..8257177404b23e131143017f3a7d835ad447f9fb 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;
}