From 9a9594c015e50f2e5bec425ef0ba6c9bd11cb674 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 | 18 ++++----- .../net/minecraft/server/WorldServer.java | 39 +++++++++++++++++++ 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index ff520d9e86..5ed02f6485 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -519,6 +519,11 @@ public class PaperWorldConfig { } } + public boolean perPlayerMobSpawns = false; + private void perPlayerMobSpawns() { + perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false); + } + public boolean antiXray; public boolean asynchronous; public EngineMode engineMode; diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 32a469025e..688573276d 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -582,9 +582,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 089a20c835..038b0f9fb1 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -130,6 +130,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); @@ -1182,6 +1183,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 ad8181784e..0c8329bfd8 100644 --- a/src/main/java/net/minecraft/server/PlayerMap.java +++ b/src/main/java/net/minecraft/server/PlayerMap.java @@ -12,6 +12,7 @@ public final class PlayerMap { public PlayerMap() {} + public Stream getPlayers(long chunkCoord) { return this.a(chunkCoord); } // Paper - OBFHELPER public Stream a(long i) { return Streams.concat(new Stream[]{this.a.stream(), this.b.stream()}); } diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java index af397dd1f7..5e001733a9 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -21,7 +21,7 @@ public final class SpawnerCreature { spawnMobs(enumcreaturetype, world, chunk, blockposition, Integer.MAX_VALUE); } - public static int spawnMobs(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition, int maxSpawns) { + public static List spawnMobs(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition, int maxSpawns) { // Paper end ChunkGenerator chunkgenerator = world.getChunkProvider().getChunkGenerator(); int i = 0; @@ -29,7 +29,7 @@ public final class SpawnerCreature { int j = blockposition1.getX(); int k = blockposition1.getY(); int l = blockposition1.getZ(); - int amountSpawned = 0; // Paper - keep track of mobs spawned + List mobsSpawned = new java.util.ArrayList<>(); // Paper if (k >= 1) { IBlockData iblockdata = world.getTypeIfLoadedAndInBounds(blockposition1); // Paper - don't load chunks for mob spawn @@ -101,7 +101,7 @@ public final class SpawnerCreature { ); if (!event.callEvent()) { if (event.shouldAbortSpawn()) { - return amountSpawned; // Paper + return mobsSpawned; // Paper } ++i2; continue; @@ -120,7 +120,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); @@ -131,16 +131,16 @@ public final class SpawnerCreature { ++i; ++i2; // Paper start - stop when limit is reached - amountSpawned++; + mobsSpawned.add(entityinsentient); } - if (amountSpawned >= maxSpawns){ - return amountSpawned; // Paper + if (mobsSpawned.size() >= maxSpawns){ + return mobsSpawned; } // Paper end // CraftBukkit end if (i >= entityinsentient.dD()) { - return amountSpawned; // Paper + return mobsSpawned; // Paper } if (entityinsentient.c(i2)) { @@ -165,7 +165,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 f7597d499f..2410db3353 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -17,6 +17,9 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; + +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -67,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; @@ -932,6 +936,7 @@ public class WorldServer extends World { } public Object2IntMap l() { + List filteredEntities = new ArrayList<>(); // Paper Object2IntMap object2intmap = new Object2IntOpenHashMap(); ObjectIterator objectiterator = this.entitiesById.values().iterator(); @@ -959,13 +964,47 @@ public class WorldServer extends World { } // Paper end object2intmap.mergeInt(enumcreaturetype, 1, Integer::sum); + filteredEntities.add(entity); // Paper } } } + // Paper start + if(this.paperConfig.perPlayerMobSpawns) { + this.playerMobTypeMap = new 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().d(); + 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