From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: jmp Date: Wed, 18 Nov 2020 20:52:25 -0800 Subject: [PATCH] Entity load/save limit per chunk Adds a config option to limit the number of entities saved and loaded to a chunk. The default values of -1 disable the limit. Although defaults are only included for certain entites, this allows setting limits for any entity type. 1.17: looks like tracking the count on loading should work fine just putting it in the EntityType#loadEntitiesRecursive, but the tracking count on save needs some more work to implement. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index 7ebc85264a2cbfb601dfe5472b561cac1a7cf8bf..486e5438254348db68017228af131cba7defd637 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import net.minecraft.world.Difficulty; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.monster.Vindicator; import net.minecraft.world.entity.monster.Zombie; import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; @@ -762,4 +763,18 @@ public class PaperWorldConfig { Difficulty.class ); } + + public Map, Integer> entityPerChunkSaveLimits = new HashMap<>(); + private void entityPerChunkSaveLimits() { + getInt("entity-per-chunk-save-limit.experience_orb", -1); + getInt("entity-per-chunk-save-limit.snowball", -1); + getInt("entity-per-chunk-save-limit.ender_pearl", -1); + getInt("entity-per-chunk-save-limit.arrow", -1); + EntityType.getEntityNameList().forEach(name -> { + final EntityType type = EntityType.getByName(name.getPath()).orElseThrow(() -> new IllegalStateException("Unknown Entity Type: " + name.toString())); + final String path = ".entity-per-chunk-save-limit." + name.getPath(); + final int value = config.getInt("world-settings." + worldName + path, config.getInt("world-settings.default" + path, -1)); // get without setting defaults + if (value != -1) entityPerChunkSaveLimits.put(type, value); + }); + } } 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 f6a814f9305813eaafa56baa0327e0111cd4e38c..30f80f8549c3236d6bfe594e323e4ca6e702005d 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 @@ -539,11 +539,22 @@ public class ChunkSerializer { chunk.setLastSaveHadEntities(false); + // Paper start + final Map, Integer> savedEntityCounts = Maps.newHashMap(); for (int j = 0; j < chunk.getEntitySlices().length; ++j) { Iterator iterator1 = chunk.getEntitySlices()[j].iterator(); while (iterator1.hasNext()) { Entity entity = (Entity) iterator1.next(); + final EntityType entityType = entity.getType(); + final int saveLimit = worldserver.paperConfig.entityPerChunkSaveLimits.getOrDefault(entityType, -1); + if (saveLimit > -1) { + if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) { + continue; + } + savedEntityCounts.merge(entityType, 1, Integer::sum); + } + // Paper end CompoundTag nbttagcompound4 = new CompoundTag(); // Paper start if (asyncsavedata == null && !entity.removed && (int) Math.floor(entity.getX()) >> 4 != chunk.getPos().x || (int) Math.floor(entity.getZ()) >> 4 != chunk.getPos().z) { @@ -674,10 +685,21 @@ public class ChunkSerializer { ListTag nbttaglist = tag.getList("Entities", 10); Level world = chunk.getLevel(); + // Paper start + final Map, Integer> loadedEntityCounts = Maps.newHashMap(); for (int i = 0; i < nbttaglist.size(); ++i) { CompoundTag nbttagcompound1 = nbttaglist.getCompound(i); EntityType.loadEntityRecursive(nbttagcompound1, world, (entity) -> { + final EntityType entityType = entity.getType(); + final int saveLimit = world.paperConfig.entityPerChunkSaveLimits.getOrDefault(entityType, -1); + if (saveLimit > -1) { + if (loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) { + return null; + } + loadedEntityCounts.merge(entityType, 1, Integer::sum); + } + // Paper end chunk.addEntity(entity); return entity; });