mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-27 02:49:19 +00:00
Merge remote-tracking branch 'origin/ver/1.21.4' into ver/1.21.4
This commit is contained in:
@@ -1,140 +0,0 @@
|
||||
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 c867796f625813797f167610ad443c4be5a7561e..04d13abd8d98f4e72732afbdbb9719d835389793 100644
|
||||
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||
@@ -190,6 +190,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 95a000dbf1a05cfd8182f15c0e0bbf7023578974..455c7e66fc1ff28c4f720a01ff11aa408446b42e 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);
|
||||
}
|
||||
|
||||
@@ -172,8 +178,13 @@ public final class NaturalSpawner {
|
||||
|
||||
Objects.requireNonNull(info);
|
||||
// Paper start - Optional per player mob spawns
|
||||
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
|
||||
- difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
|
||||
+ // Paper start - throttle failed spawn attempts
|
||||
+ if (NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null) == 0) {
|
||||
+ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()]++;
|
||||
+ } else {
|
||||
+ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()] = 0;
|
||||
+ }
|
||||
+ // Paper end - throttle failed spawn attempts
|
||||
// Paper end - Optional per player mob spawns
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -12,18 +12,10 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on
|
||||
under 625 villagers situation.
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
|
||||
index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc66b5bcc0 100644
|
||||
index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..9ef531551bb135d4b6a1ca9c3320e35fa62d7c41 100644
|
||||
--- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
|
||||
+++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.minecraft.world.entity.ai.sensing;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
-import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -13,18 +12,92 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
|
||||
@@ -13,18 +13,92 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T> {
|
||||
@@ -63,7 +55,7 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc
|
||||
+ bits[i] = Double.doubleToRawLongBits(reference.distanceToSqr(entities[i]));
|
||||
+ }
|
||||
+
|
||||
+ fastRadixSort(entities, bits, 0, entities.length-1, 62);
|
||||
+ fastRadixSort(entities, bits, 0, entities.length - 1, 62);
|
||||
+ return entities;
|
||||
+ }
|
||||
+
|
||||
@@ -87,19 +79,19 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (low < j) fastRadixSort(ents, bits, low, j, bit-1);
|
||||
+ if (i < high) fastRadixSort(ents, bits, i, high, bit-1);
|
||||
+ if (low < j) fastRadixSort(ents, bits, low, j, bit - 1);
|
||||
+ if (i < high) fastRadixSort(ents, bits, i, high, bit - 1);
|
||||
+ }
|
||||
+
|
||||
+ private void insertionSort(LivingEntity[] ents, long[] bits, int low, int high) {
|
||||
+ for (int i = low+1; i <= high; i++) {
|
||||
+ for (int i = low + 1; i <= high; i++) {
|
||||
+ int j = i;
|
||||
+ LivingEntity e = ents[j];
|
||||
+ long b = bits[j];
|
||||
+
|
||||
+ while (j > low && bits[j-1] > b) {
|
||||
+ ents[j] = ents[j-1];
|
||||
+ bits[j] = bits[j-1];
|
||||
+ while (j > low && bits[j - 1] > b) {
|
||||
+ ents[j] = ents[j - 1];
|
||||
+ bits[j] = bits[j - 1];
|
||||
+ j--;
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -9,7 +9,7 @@ Paper: 3859ms
|
||||
Leaf : 1363ms (-64% reduction)
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java b/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
|
||||
index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4b59613d6 100644
|
||||
index 13359e484486a5280f408955fe2a365cd3c34a43..48c30c8b686fdc82481bd72d6d10f541e9eb0217 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java
|
||||
@@ -9,7 +9,7 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
|
||||
@@ -17,16 +17,17 @@ index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4
|
||||
public class SetLookAndInteract {
|
||||
public static BehaviorControl<LivingEntity> create(EntityType<?> entityType, int maxDist) {
|
||||
- int i = maxDist * maxDist;
|
||||
+ final int maxDistSq = maxDist * maxDist;
|
||||
+ final int maxDistSq = maxDist * maxDist; // Leaf - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
return BehaviorBuilder.create(
|
||||
instance -> instance.group(
|
||||
instance.registered(MemoryModuleType.LOOK_TARGET),
|
||||
@@ -19,16 +19,20 @@ public class SetLookAndInteract {
|
||||
@@ -19,16 +19,22 @@ public class SetLookAndInteract {
|
||||
.apply(
|
||||
instance,
|
||||
(lookTarget, interactionTarget, nearestVisibleLivingEntities) -> (level, entity, gameTime) -> {
|
||||
- Optional<LivingEntity> optional = instance.<NearestVisibleLivingEntities>get(nearestVisibleLivingEntities)
|
||||
- .findClosest(nearEntity -> nearEntity.distanceToSqr(entity) <= i && entityType.equals(nearEntity.getType()));
|
||||
+ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
+ // Check entity type first as it's likely cheaper than distance calculation
|
||||
+ NearestVisibleLivingEntities entities = instance.get(nearestVisibleLivingEntities);
|
||||
+ Optional<LivingEntity> optional = entities.findClosest(
|
||||
@@ -46,33 +47,22 @@ index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4
|
||||
+ interactionTarget.set(livingEntity);
|
||||
+ lookTarget.set(new EntityTracker(livingEntity, true));
|
||||
+ return true;
|
||||
+ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
}
|
||||
)
|
||||
);
|
||||
diff --git a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
|
||||
index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90aaa6e6af 100644
|
||||
index 2b973a3ba7d65330fa4690e71e5321c28457ec61..82de8e3bc462638bffbb43456076de2670d76540 100644
|
||||
--- a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
|
||||
+++ b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java
|
||||
@@ -2,10 +2,11 @@ package net.minecraft.world.entity.ai.memory;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
||||
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
+import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
-import java.util.stream.Stream;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor;
|
||||
@@ -32,9 +33,26 @@ public class NearestVisibleLivingEntities {
|
||||
@@ -32,11 +32,30 @@ public class NearestVisibleLivingEntities {
|
||||
}
|
||||
|
||||
public Optional<LivingEntity> findClosest(Predicate<LivingEntity> predicate) {
|
||||
- for (LivingEntity livingEntity : this.nearbyEntities) {
|
||||
- if (predicate.test(livingEntity) && this.lineOfSightTest.test(livingEntity)) {
|
||||
- return Optional.of(livingEntity);
|
||||
+ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
+ // Early return if no entities
|
||||
+ if (this.nearbyEntities.isEmpty()) {
|
||||
+ return Optional.empty();
|
||||
@@ -95,15 +85,21 @@ index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90
|
||||
+ }
|
||||
}
|
||||
}
|
||||
+ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
|
||||
@@ -45,8 +63,14 @@ public class NearestVisibleLivingEntities {
|
||||
return Iterables.filter(this.nearbyEntities, target -> predicate.test(target) && this.lineOfSightTest.test(target)); // Leaf - Optimize baby villager sensor - diff on change
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -46,8 +65,20 @@ public class NearestVisibleLivingEntities {
|
||||
}
|
||||
|
||||
- public Stream<LivingEntity> find(Predicate<LivingEntity> predicate) {
|
||||
public Stream<LivingEntity> find(Predicate<LivingEntity> predicate) {
|
||||
- return this.nearbyEntities.stream().filter(target -> predicate.test(target) && this.lineOfSightTest.test(target));
|
||||
+ public List<LivingEntity> find(Predicate<LivingEntity> predicate) {
|
||||
+ ObjectList<LivingEntity> result = new ObjectArrayList<>();
|
||||
+ return this.nearbyEntities.stream().filter(target -> predicate.test(target) && this.lineOfSightTest.test(target)); // Leaf - Optimize SetLookAndInteract and NearestVisibleLivingEntities - diff on change
|
||||
+ }
|
||||
+
|
||||
+ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
+ public List<LivingEntity> findWithList(Predicate<LivingEntity> predicate) {
|
||||
+ it.unimi.dsi.fastutil.objects.ObjectList<LivingEntity> result = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
||||
+ for (LivingEntity entity : this.nearbyEntities) {
|
||||
+ if (predicate.test(entity) && this.lineOfSightTest.test(entity)) {
|
||||
+ result.add(entity);
|
||||
@@ -111,5 +107,7 @@ index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90
|
||||
+ }
|
||||
+ return result;
|
||||
}
|
||||
+ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities
|
||||
|
||||
public boolean contains(LivingEntity entity) {
|
||||
return this.nearbyEntities.contains(entity) && this.lineOfSightTest.test(entity);
|
||||
|
||||
@@ -7,27 +7,20 @@ This method is maninly visible when ton of villagers suddenly wants to sleep
|
||||
Safe optimization, no need to provide any numbers.
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java b/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java
|
||||
index cbde74f4b6d586a5f80cdd675573441636bf682d..3ac3059ce23d6291aa4e96ed5441159133d3e965 100644
|
||||
index cbde74f4b6d586a5f80cdd675573441636bf682d..2324b941c7513692608b5146bc90c8e6124d2ee6 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java
|
||||
@@ -2,6 +2,7 @@ package net.minecraft.world.entity.ai.behavior;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
+import java.util.ArrayList;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.PathfinderMob;
|
||||
@@ -20,16 +21,29 @@ public class InsideBrownianWalk {
|
||||
@@ -20,16 +20,31 @@ public class InsideBrownianWalk {
|
||||
return false;
|
||||
} else {
|
||||
BlockPos blockPos = mob.blockPosition();
|
||||
- List<BlockPos> list = BlockPos.betweenClosedStream(blockPos.offset(-1, -1, -1), blockPos.offset(1, 1, 1))
|
||||
- .map(BlockPos::immutable)
|
||||
- .collect(Util.toMutableList());
|
||||
+ // Leaf start - Remove streams on InsideBrownianWalk
|
||||
+ BlockPos minPos = blockPos.offset(-1, -1, -1);
|
||||
+ BlockPos maxPos = blockPos.offset(1, 1, 1);
|
||||
+ List<BlockPos> list = new ArrayList<>();
|
||||
+ List<BlockPos> list = new java.util.ArrayList<>();
|
||||
+
|
||||
+ for (int x = minPos.getX(); x <= maxPos.getX(); x++) {
|
||||
+ for (int y = minPos.getY(); y <= maxPos.getY(); y++) {
|
||||
@@ -53,6 +46,7 @@ index cbde74f4b6d586a5f80cdd675573441636bf682d..3ac3059ce23d6291aa4e96ed54411591
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Remove streams on InsideBrownianWalk
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Wed, 19 Mar 2025 13:32:39 -0400
|
||||
Subject: [PATCH] Validate recipe display index before retrieving it
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
index 4bd1b514f91c0a2c9261b41211a4a341f784a995..5b69c4927174611d62f0f4698215ab97c827c7f5 100644
|
||||
--- a/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
+++ b/net/minecraft/world/item/crafting/RecipeManager.java
|
||||
@@ -206,7 +206,11 @@ public class RecipeManager extends SimplePreparableReloadListener<RecipeMap> imp
|
||||
|
||||
@Nullable
|
||||
public RecipeManager.ServerDisplayInfo getRecipeFromDisplay(RecipeDisplayId display) {
|
||||
- return this.allDisplays.get(display.index());
|
||||
+ // Leaf start - Validate recipe display index before retrieving it
|
||||
+ final int index = display.index();
|
||||
+
|
||||
+ return this.allDisplays.size() > index ? this.allDisplays.get(index) : null;
|
||||
+ // Leaf end - Validate recipe display index before retrieving it
|
||||
}
|
||||
|
||||
public void listDisplaysForRecipe(ResourceKey<Recipe<?>> recipe, Consumer<RecipeDisplayEntry> output) {
|
||||
@@ -0,0 +1,320 @@
|
||||
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 d3f5242fc66529bf3137da4d505a6cf55e749e43..ce2621a87dec1befb016b3437ceb2d02ed6d0b75 100644
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -164,10 +164,21 @@ public final class NaturalSpawner {
|
||||
// Copied from getFilteredSpawningCategories
|
||||
int limit = mobCategory.getMaxInstancesPerChunk();
|
||||
SpawnCategory spawnCategory = 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 (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 =
|
||||
@@ -181,12 +192,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
|
||||
}
|
||||
}
|
||||
@@ -210,12 +229,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
|
||||
@@ -235,15 +263,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();
|
||||
@@ -284,13 +320,13 @@ 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 && filter.test(spawnerData.type, mutableBlockPos, chunk)) {
|
||||
// Paper end - PreCreatureSpawnEvent
|
||||
Mob mobForSpawn = getMobForSpawn(level, spawnerData.type);
|
||||
if (mobForSpawn == null) {
|
||||
- return;
|
||||
+ return i; // Paper - throttle failed spawn attempts
|
||||
}
|
||||
|
||||
mobForSpawn.moveTo(d, y, d1, level.random.nextFloat() * 360.0F, 0.0F);
|
||||
@@ -313,7 +349,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)) {
|
||||
@@ -326,6 +362,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 3a6db5bc0c8be7d68e15317a621c1965fdc3a9bd..50a9903367f49ece2a267d10944b1515c7b93859 100644
|
||||
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
@@ -91,6 +91,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 c0939c311c554a4660b80725294663bab7915733..e2df93b2500a74c4cecac1515f3991967a07a052 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -92,6 +92,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
|
||||
@@ -216,6 +217,19 @@ public record SerializableChunkData(
|
||||
lists[i] = list4;
|
||||
}
|
||||
|
||||
+ // Paper start - throttle failed spawn attempts
|
||||
+ long[] failedSpawnAttemptsData = null;
|
||||
+ if (tag.contains("Paper.FailedSpawnAttempts", net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
+ failedSpawnAttemptsData = new long[net.minecraft.world.entity.MobCategory.values().length];
|
||||
+ CompoundTag failedSpawnAttemptsTag = tag.getCompound("Paper.FailedSpawnAttempts");
|
||||
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) {
|
||||
+ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName(), net.minecraft.nbt.Tag.TAG_LONG)) {
|
||||
+ failedSpawnAttemptsData[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLong(mobCategory.getSerializedName());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - throttle failed spawn attempts
|
||||
+
|
||||
List<CompoundTag> list5 = Lists.transform(tag.getList("entities", 10), tag1 -> (CompoundTag)tag1);
|
||||
List<CompoundTag> list6 = Lists.transform(tag.getList("block_entities", 10), tag1 -> (CompoundTag)tag1);
|
||||
CompoundTag compound1 = tag.getCompound("structures");
|
||||
@@ -294,6 +308,7 @@ public record SerializableChunkData(
|
||||
list6,
|
||||
compound1
|
||||
, tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
|
||||
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -450,6 +465,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 {
|
||||
@@ -603,6 +627,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,
|
||||
@@ -623,6 +648,7 @@ public record SerializableChunkData(
|
||||
list1,
|
||||
compoundTag
|
||||
, persistentDataContainer // CraftBukkit - persistentDataContainer
|
||||
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -719,6 +745,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
|
||||
@@ -947,4 +988,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
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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 7bfa7aa30c1181587c7632f920f48348d2493ea4..d838c90f98c6593404c77d0aab8655c0d15905c4 100644
|
||||
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||
@@ -181,6 +181,17 @@ 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)));
|
||||
|
||||
+ // Paper start - throttle failed spawn attempts
|
||||
+ 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)));
|
||||
+ }
|
||||
+ // Paper end - throttle failed spawn attempts
|
||||
+
|
||||
@ConfigSerializable
|
||||
public record DespawnRangePair(@Required DespawnRange hard, @Required DespawnRange soft) {
|
||||
public static DespawnRangePair createDefault() {
|
||||
3
todos.md
3
todos.md
@@ -6,11 +6,8 @@
|
||||
|
||||
# Leaf TODOs
|
||||
- [ ] refactor leaves protocol manager opt and pr it.
|
||||
- [ ] Check and apply work patches
|
||||
- [ ] Transfer patch notes to file for Gale and Leaf
|
||||
- [ ] Add spigot config unknown message to leaf docs
|
||||
- [ ] Add server full join config explanation to docs
|
||||
- [ ] Add purpur config changes to docs moved config
|
||||
- [ ] Merge changes from Tramontane
|
||||
- [ ] check Dont send useless entity packets
|
||||
- [ ] Check lithium fastutil and equipment tracking missing stuff?
|
||||
|
||||
Reference in New Issue
Block a user