diff --git a/patches/server/0055-implement-optional-per-player-mob-spawns.patch b/patches/server/0055-implement-optional-per-player-mob-spawns.patch new file mode 100644 index 000000000..9563e88d2 --- /dev/null +++ b/patches/server/0055-implement-optional-per-player-mob-spawns.patch @@ -0,0 +1,243 @@ +From 961e437ba0b5a20ba5bb6de2518c686f39df9afd Mon Sep 17 00:00:00 2001 +From: kickash32 +Date: Tue, 11 Jun 2019 22:22:16 -0400 +Subject: [PATCH] implement optional per player mob spawns + +--- + .../destroystokyo/paper/PaperWorldConfig.java | 5 +++ + .../minecraft/server/ChunkProviderServer.java | 16 +++++++-- + .../net/minecraft/server/PlayerChunkMap.java | 10 ++++++ + .../java/net/minecraft/server/PlayerMap.java | 1 + + .../net/minecraft/server/SpawnerCreature.java | 21 ++++++----- + .../net/minecraft/server/WorldServer.java | 36 +++++++++++++++++++ + 6 files changed, 76 insertions(+), 13 deletions(-) + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index ff520d9e86..c2823c10f9 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -509,6 +509,11 @@ public class PaperWorldConfig { + maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); + } + ++ public boolean perPlayerMobSpawns = false; ++ private void perPlayerMobSpawns() { ++ perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false); ++ } ++ + public boolean countAllMobsForSpawning = false; + private void countAllMobsForSpawning() { + countAllMobsForSpawning = getBoolean("count-all-mobs-for-spawning", false); +diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java +index 7ade9a53b4..4c18668a9a 100644 +--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java ++++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java +@@ -628,9 +628,21 @@ public class ChunkProviderServer extends IChunkProvider { + // Paper start - only allow spawns upto the limit per chunk and update count afterwards + int currEntityCount = object2intmap.getInt(enumcreaturetype); + int difference = k1 - currEntityCount; ++ if(this.world.paperConfig.perPlayerMobSpawns){ ++ final int[] min = {Integer.MAX_VALUE}; ++ final int finalLimit = limit; ++ playerChunkMap.getPlayersNear(chunk.getPos(), this.world.spigotConfig.mobSpawnRange) ++ .forEach((entityplayer) -> min[0] = Math.min( ++ finalLimit - ((WorldServer)chunk.getWorld()).getMobCountNear(entityplayer, enumcreaturetype), ++ min[0])); ++ difference = (min[0] == Integer.MAX_VALUE) ? 0 : min[0]; ++ } ++ + if (difference > 0) { +- object2intmap.put(enumcreaturetype, currEntityCount + SpawnerCreature.spawnMobs(enumcreaturetype, world, chunk, blockposition, difference)); +- // Paper end ++ List spawned = SpawnerCreature.spawnMobs(enumcreaturetype, this.world, chunk, blockposition, difference); ++ object2intmap.put(enumcreaturetype, currEntityCount + spawned.size()); ++ this.world.updatePlayerMobTypeMap(spawned); ++ // Paper end + } + } + } +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index 965c104b7b..ad6402f723 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -137,6 +137,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + this.setViewDistance(i); + } + ++ private static double squareDist(ChunkCoordIntPair chunkcoord, Entity entity) { return a(chunkcoord, entity); } // Paper - OBFHELPER + private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) { + double d0 = (double) (chunkcoordintpair.x * 16 + 8); + double d1 = (double) (chunkcoordintpair.z * 16 + 8); +@@ -1330,6 +1331,15 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + + } + ++ // Paper start ++ public Stream getPlayersNear(ChunkCoordIntPair coordIntPair, int rangeChunks) { ++ int squareRangeBlocks = ((rangeChunks << 4) * (rangeChunks << 4)); ++ ++ return this.playerMap.getPlayers(coordIntPair.pair()).filter( (entityplayer) -> ++ (!entityplayer.isSpectator() && squareDist(coordIntPair, entityplayer) < squareRangeBlocks)); ++ } ++ // Paper end ++ + @Override + public Stream a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { + return this.playerMap.a(chunkcoordintpair.pair()).filter((entityplayer) -> { +diff --git a/src/main/java/net/minecraft/server/PlayerMap.java b/src/main/java/net/minecraft/server/PlayerMap.java +index f386c4e997..c896a0b85f 100644 +--- a/src/main/java/net/minecraft/server/PlayerMap.java ++++ b/src/main/java/net/minecraft/server/PlayerMap.java +@@ -10,6 +10,7 @@ public final class PlayerMap { + + public PlayerMap() {} + ++ public Stream getPlayers(long chunkCoord) { return this.a(chunkCoord); } // Paper - OBFHELPER + public Stream a(long i) { + return this.a.keySet().stream(); + } +diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java +index 925efd4a15..70580355c6 100644 +--- a/src/main/java/net/minecraft/server/SpawnerCreature.java ++++ b/src/main/java/net/minecraft/server/SpawnerCreature.java +@@ -20,16 +20,15 @@ public final class SpawnerCreature { + public static void a(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition) { + spawnMobs(enumcreaturetype, world, chunk, blockposition, Integer.MAX_VALUE); + } +- +- public static int spawnMobs(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition, int maxSpawns) { +- // Paper end ++ public static List spawnMobs(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition, int maxSpawns) { ++ List mobsSpawned = new java.util.ArrayList<>(); ++ // Paper end + ChunkGenerator chunkgenerator = world.getChunkProvider().getChunkGenerator(); + int i = 0; + BlockPosition blockposition1 = getRandomPosition(world, chunk); + int j = blockposition1.getX(); + int k = blockposition1.getY(); + int l = blockposition1.getZ(); +- int amountSpawned = 0; // Paper - keep track of mobs spawned + + if (k >= 1) { + IBlockData iblockdata = world.getTypeIfLoadedAndInBounds(blockposition1); // Paper - don't load chunks for mob spawn +@@ -104,7 +103,7 @@ public final class SpawnerCreature { + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { +- return amountSpawned; // Paper ++ return mobsSpawned; // Paper + } + ++i2; + continue; +@@ -123,7 +122,7 @@ public final class SpawnerCreature { + } catch (Exception exception) { + SpawnerCreature.LOGGER.warn("Failed to create mob", exception); + ServerInternalException.reportInternalException(exception); // Paper +- return amountSpawned; // Paper ++ return mobsSpawned; // Paper + } + + entityinsentient.setPositionRotation((double) f, (double) k, (double) f1, world.random.nextFloat() * 360.0F, 0.0F); +@@ -134,15 +133,15 @@ public final class SpawnerCreature { + ++i; + ++i2; + // Paper start - stop when limit is reached +- ++amountSpawned; ++ mobsSpawned.add(entityinsentient); + } +- if (amountSpawned >= maxSpawns) { +- return amountSpawned; ++ if (mobsSpawned.size() >= maxSpawns) { ++ return mobsSpawned; + } + // Paper end + // CraftBukkit end + if (i >= entityinsentient.dC()) { +- return amountSpawned; // Paper ++ return mobsSpawned; // Paper + } + + if (entityinsentient.c(i2)) { +@@ -168,7 +167,7 @@ public final class SpawnerCreature { + + } + } +- return amountSpawned; // Paper ++ return mobsSpawned; // Paper + } + + @Nullable +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index 83e47b6ad2..f6303fad5a 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -70,6 +70,7 @@ public class WorldServer extends World { + private boolean ticking; + @Nullable + private final MobSpawnerTrader mobSpawnerTrader; ++ private Map> playerMobTypeMap; // Paper + + // CraftBukkit start + private int tickPosition; +@@ -931,6 +932,7 @@ public class WorldServer extends World { + } + + public Object2IntMap l() { ++ List filteredEntities = new java.util.ArrayList<>(); // Paper + Object2IntMap object2intmap = new Object2IntOpenHashMap(); + ObjectIterator objectiterator = this.entitiesById.values().iterator(); + +@@ -949,6 +951,7 @@ public class WorldServer extends World { + EnumCreatureType enumcreaturetype = entity.getEntityType().e(); + + if (enumcreaturetype != EnumCreatureType.MISC && this.getChunkProvider().b(entity)) { ++ filteredEntities.add(entity); // Paper + // Paper start - Only count natural spawns + if (!this.paperConfig.countAllMobsForSpawning && + !(entity.spawnReason == CreatureSpawnEvent.SpawnReason.NATURAL || +@@ -960,9 +963,42 @@ public class WorldServer extends World { + } + } + ++ // Paper start ++ if(this.paperConfig.perPlayerMobSpawns) { ++ this.playerMobTypeMap = new java.util.HashMap<>(); ++ updatePlayerMobTypeMap(filteredEntities); ++ } ++ // Paper end + return object2intmap; + } + ++ // Paper start ++ public void updatePlayerMobTypeMap(List entities) { ++ if (this.playerMobTypeMap == null) { return; } ++ for (Entity entity : entities) { ++ ((ChunkProviderServer) this.chunkProvider).playerChunkMap.getPlayersNear(entity.getChunkAtLocation().getPos(), this.spigotConfig.mobSpawnRange).forEach( (player) -> { ++ if (!this.playerMobTypeMap.containsKey(player.uniqueID)) { this.playerMobTypeMap.put(player.uniqueID, new Object2IntOpenHashMap()); } ++ Map tmpMap = this.playerMobTypeMap.get(player.uniqueID); ++ EnumCreatureType enumType = entity.getEntityType().e(); ++ tmpMap.put( ++ enumType, ++ tmpMap.getOrDefault(enumType, 0) + 1); ++ }); ++ } ++ } ++ ++ public int getMobCountNear(EntityPlayer entityPlayer, EnumCreatureType enumCreatureType) { ++ int count = 0; ++ if (this.playerMobTypeMap != null) { ++ Map map = this.playerMobTypeMap.get(entityPlayer.uniqueID); ++ if (map != null) { ++ count = map.getOrDefault(enumCreatureType, 0); ++ } ++ } ++ return count; ++ } ++ // Paper end ++ + @Override + public boolean addEntity(Entity entity) { + // CraftBukkit start +-- +2.20.1 +