mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-27 19:09:22 +00:00
ClassInstanceMultiMap belongs to Minecraft vanilla entity storage. And is unused, since replaced by spottedleaf's entity storage (rewrite chunk system). However these patches might be useful for vanilla entity storage if is used.
322 lines
18 KiB
Diff
322 lines
18 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/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
|
index 345d4b80bd4383e0fb66d744d87bc8ef4100fd32..a2da4fce50f31d56036d04041c4f80ed90c18b27 100644
|
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -158,10 +158,21 @@ public final class NaturalSpawner {
|
|
// Copied from getFilteredSpawningCategories
|
|
int limit = mobCategory.getMaxInstancesPerChunk();
|
|
org.bukkit.entity.SpawnCategory spawnCategory = org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(mobCategory);
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ boolean spawnThisTick = true;
|
|
+ long ticksPerSpawn = level.ticksPerSpawnCategory.getLong(spawnCategory);
|
|
+ long ticksPerSpawnTmp = ticksPerSpawn;
|
|
+ io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.SpawningThrottle spawningThrottle = level.paperConfig().entities.spawning.spawningThrottle;
|
|
+ if (spawningThrottle.failedAttemptsThreshold.test(threshold -> chunk.failedSpawnAttempts[mobCategory.ordinal()] > threshold)) {
|
|
+ ticksPerSpawn = Math.max(ticksPerSpawn, spawningThrottle.throttledTicksPerSpawn.getOrDefault(mobCategory, -1));
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
if (org.bukkit.craftbukkit.util.CraftSpawnCategory.isValidForLimits(spawnCategory)) {
|
|
+ spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts
|
|
limit = level.getWorld().getSpawnLimit(spawnCategory);
|
|
}
|
|
|
|
+ if (!spawningThrottle.failedAttemptsThreshold.enabled() || spawnThisTick) { // Paper - throttle failed spawn attempts
|
|
// Apply per-player limit
|
|
int minDiff = Integer.MAX_VALUE;
|
|
final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
|
|
@@ -175,12 +186,20 @@ public final class NaturalSpawner {
|
|
|
|
maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
|
|
canSpawn = maxSpawns > 0;
|
|
+ } else { canSpawn = false; } // Paper - throttle failed spawn attempts
|
|
} else {
|
|
canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos());
|
|
}
|
|
if (canSpawn) {
|
|
- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn,
|
|
- maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ int spawnCount = spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn,
|
|
+ maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null, false);
|
|
+ if (spawnCount == 0) {
|
|
+ chunk.failedSpawnAttempts[mobCategory.ordinal()]++;
|
|
+ } else {
|
|
+ chunk.failedSpawnAttempts[mobCategory.ordinal()] = 0;
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
// Paper end - Optional per player mob spawns
|
|
}
|
|
}
|
|
@@ -204,12 +223,21 @@ public final class NaturalSpawner {
|
|
}
|
|
public static void spawnCategoryForChunk(
|
|
MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ ) {
|
|
+ spawnCategoryForChunk(category, level, chunk, filter, callback, maxSpawns, trackEntity, false);
|
|
+ }
|
|
+ public static int spawnCategoryForChunk(
|
|
+ MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity, final boolean nothing
|
|
+ // Paper end - throttle failed spawn attempts
|
|
) {
|
|
// Paper end - Optional per player mob spawns
|
|
BlockPos randomPosWithin = getRandomPosWithin(level, chunk);
|
|
if (randomPosWithin.getY() >= level.getMinY() + 1) {
|
|
- spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
|
|
+ return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts
|
|
}
|
|
+
|
|
+ return 0; // Paper - throttle failed spawn attempts
|
|
}
|
|
|
|
@VisibleForDebug
|
|
@@ -229,15 +257,23 @@ public final class NaturalSpawner {
|
|
}
|
|
public static void spawnCategoryForPosition(
|
|
MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ ) {
|
|
+ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false);
|
|
+ }
|
|
+ public static int spawnCategoryForPosition(
|
|
+ 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 - Optional per player mob spawns
|
|
StructureManager structureManager = level.structureManager();
|
|
ChunkGenerator generator = level.getChunkSource().getGenerator();
|
|
int y = pos.getY();
|
|
+ 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();
|
|
- int i = 0;
|
|
+ //int i = 0; // Paper - throttle failed spawn attempts - move up
|
|
|
|
for (int i1 = 0; i1 < 3; i1++) {
|
|
int x = pos.getX();
|
|
@@ -278,14 +314,14 @@ public final class NaturalSpawner {
|
|
}
|
|
// Paper end - per player mob count backoff
|
|
if (doSpawning == PreSpawnStatus.ABORT) {
|
|
- return;
|
|
+ return i; // Paper - throttle failed spawn attempts
|
|
}
|
|
if (doSpawning == PreSpawnStatus.SUCCESS
|
|
// Paper end - PreCreatureSpawnEvent
|
|
&& filter.test(spawnerData.type(), mutableBlockPos, chunk)) {
|
|
Mob mobForSpawn = getMobForSpawn(level, spawnerData.type());
|
|
if (mobForSpawn == null) {
|
|
- return;
|
|
+ return i; // Paper - throttle failed spawn attempts
|
|
}
|
|
|
|
mobForSpawn.snapTo(d, y, d1, level.random.nextFloat() * 360.0F, 0.0F);
|
|
@@ -308,7 +344,7 @@ public final class NaturalSpawner {
|
|
}
|
|
// CraftBukkit end
|
|
if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns
|
|
- return;
|
|
+ return i; // Paper - throttle failed spawn attempts
|
|
}
|
|
|
|
if (mobForSpawn.isMaxGroupSizeReached(i3)) {
|
|
@@ -321,6 +357,8 @@ public final class NaturalSpawner {
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+ return i; // Paper - throttle failed spawn attempts
|
|
}
|
|
|
|
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) {
|
|
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
index 686c031ec73acc80683aaa39a78fe9221f0215a6..d7dbe7d24a6697be6b5db729f940b16b74838d11 100644
|
|
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
|
|
@@ -92,6 +92,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
|
|
private boolean slimeChunk;
|
|
private boolean hasComputedSlimeChunk;
|
|
// Leaf end - Matter - Secure Seed
|
|
+ public final long[] failedSpawnAttempts = new long[net.minecraft.world.entity.MobCategory.values().length]; // Paper - throttle failed spawn attempts
|
|
|
|
// Paper start - rewrite chunk system
|
|
private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles;
|
|
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
|
index e04d3479383cd480cf35ed7ac3c82e7f6fb69e28..ab1fc7ca0639f9e71933f623258bb8d123988262 100644
|
|
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
|
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
|
@@ -94,6 +94,7 @@ public record SerializableChunkData(
|
|
List<CompoundTag> blockEntities,
|
|
CompoundTag structureData
|
|
, @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
|
|
+ , @Nullable long[] failedSpawnAttempts // Paper - throttle failed spawn attempts
|
|
) {
|
|
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
|
|
Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray
|
|
@@ -190,6 +191,19 @@ public record SerializableChunkData(
|
|
lists[i] = list2;
|
|
}
|
|
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ long[] failedSpawnAttemptsData = null;
|
|
+ if (tag.contains("Paper.FailedSpawnAttempts")) {
|
|
+ failedSpawnAttemptsData = new long[net.minecraft.world.entity.MobCategory.values().length];
|
|
+ CompoundTag failedSpawnAttemptsTag = tag.getCompoundOrEmpty("Paper.FailedSpawnAttempts");
|
|
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) {
|
|
+ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName())) {
|
|
+ failedSpawnAttemptsData[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLongOr(mobCategory.getSerializedName(), 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
+
|
|
List<CompoundTag> list3 = tag.getList("entities").stream().flatMap(ListTag::compoundStream).toList();
|
|
List<CompoundTag> list4 = tag.getList("block_entities").stream().flatMap(ListTag::compoundStream).toList();
|
|
CompoundTag compoundOrEmpty = tag.getCompoundOrEmpty("structures");
|
|
@@ -270,6 +284,7 @@ public record SerializableChunkData(
|
|
list4,
|
|
compoundOrEmpty
|
|
, tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
|
|
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
|
|
);
|
|
}
|
|
}
|
|
@@ -426,6 +441,15 @@ public record SerializableChunkData(
|
|
chunkAccess.addPackedPostProcess(this.postProcessingSections[i], i);
|
|
}
|
|
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ long[] failedSpawnAttemptsData = this.failedSpawnAttempts;
|
|
+ if (failedSpawnAttemptsData != null) {
|
|
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) {
|
|
+ System.arraycopy(failedSpawnAttemptsData, 0, chunkAccess.failedSpawnAttempts, 0, failedSpawnAttemptsData.length);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
+
|
|
if (chunkType == ChunkType.LEVELCHUNK) {
|
|
return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight
|
|
} else {
|
|
@@ -556,6 +580,7 @@ public record SerializableChunkData(
|
|
persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
|
|
}
|
|
// CraftBukkit end
|
|
+ final long[] failedSpawnAttemptsData = chunk.failedSpawnAttempts; // Paper - throttle failed spawn attempts
|
|
return new SerializableChunkData(
|
|
level.registryAccess().lookupOrThrow(Registries.BIOME),
|
|
pos,
|
|
@@ -576,6 +601,7 @@ public record SerializableChunkData(
|
|
list1,
|
|
compoundTag
|
|
, persistentDataContainer // CraftBukkit - persistentDataContainer
|
|
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
|
|
);
|
|
}
|
|
}
|
|
@@ -660,6 +686,21 @@ public record SerializableChunkData(
|
|
compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
|
|
}
|
|
// CraftBukkit end
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ CompoundTag failedSpawnAttemptsTag = new CompoundTag();
|
|
+ long[] failedSpawnAttemptsData = this.failedSpawnAttempts;
|
|
+ if (failedSpawnAttemptsData != null) {
|
|
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) {
|
|
+ long failedAttempts = failedSpawnAttemptsData[mobCategory.ordinal()];
|
|
+ if (failedAttempts > 0) {
|
|
+ failedSpawnAttemptsTag.putLong(mobCategory.getSerializedName(), failedAttempts);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!failedSpawnAttemptsTag.isEmpty()) {
|
|
+ compoundTag.put("Paper.FailedSpawnAttempts", failedSpawnAttemptsTag);
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
// Paper start - starlight
|
|
if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) {
|
|
// clobber vanilla value to force vanilla to relight
|
|
@@ -875,4 +916,50 @@ public record SerializableChunkData(
|
|
}
|
|
// Paper end - starlight - convert from record
|
|
}
|
|
+
|
|
+ // Paper start - throttle failed spawn attempts
|
|
+ // For plugin compatibility
|
|
+ public SerializableChunkData(
|
|
+ Registry<Biome> biomeRegistry,
|
|
+ ChunkPos chunkPos,
|
|
+ int minSectionY,
|
|
+ long lastUpdateTime,
|
|
+ long inhabitedTime,
|
|
+ ChunkStatus chunkStatus,
|
|
+ @Nullable BlendingData.Packed blendingData,
|
|
+ @Nullable BelowZeroRetrogen belowZeroRetrogen,
|
|
+ UpgradeData upgradeData,
|
|
+ @Nullable long[] carvingMask,
|
|
+ Map<Heightmap.Types, long[]> heightmaps,
|
|
+ ChunkAccess.PackedTicks packedTicks,
|
|
+ ShortList[] postProcessingSections,
|
|
+ boolean lightCorrect,
|
|
+ List<net.minecraft.world.level.chunk.storage.SerializableChunkData.SectionData> sectionData,
|
|
+ List<CompoundTag> entities,
|
|
+ List<CompoundTag> blockEntities,
|
|
+ CompoundTag structureData,
|
|
+ @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
|
|
+ ) {
|
|
+ this(biomeRegistry,
|
|
+ chunkPos,
|
|
+ minSectionY,
|
|
+ lastUpdateTime,
|
|
+ inhabitedTime,
|
|
+ chunkStatus,
|
|
+ blendingData,
|
|
+ belowZeroRetrogen,
|
|
+ upgradeData,
|
|
+ carvingMask,
|
|
+ heightmaps,
|
|
+ packedTicks,
|
|
+ postProcessingSections,
|
|
+ lightCorrect,
|
|
+ sectionData,
|
|
+ entities,
|
|
+ blockEntities,
|
|
+ structureData,
|
|
+ persistentDataContainer,
|
|
+ null);
|
|
+ }
|
|
+ // Paper end - throttle failed spawn attempts
|
|
}
|