aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0999-Optional-per-player-mob-spawns.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0999-Optional-per-player-mob-spawns.patch')
-rw-r--r--patches/server/0999-Optional-per-player-mob-spawns.patch253
1 files changed, 253 insertions, 0 deletions
diff --git a/patches/server/0999-Optional-per-player-mob-spawns.patch b/patches/server/0999-Optional-per-player-mob-spawns.patch
new file mode 100644
index 0000000000..807e87ea3f
--- /dev/null
+++ b/patches/server/0999-Optional-per-player-mob-spawns.patch
@@ -0,0 +1,253 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kickash32 <[email protected]>
+Date: Mon, 19 Aug 2019 01:27:58 +0500
+Subject: [PATCH] Optional per player mob spawns
+
+
+diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
+index d843bc04ae93d11d7820cab5ed18617193568f0d..152cd6b1671785b495caeb7e535f58df864ce24c 100644
+--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
+@@ -256,8 +256,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ return this.nearbyPlayers;
+ }
+
++ // Paper start - Optional per player mob spawns
++ public void updatePlayerMobTypeMap(final Entity entity) {
++ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
++ return;
++ }
++ final int index = entity.getType().getCategory().ordinal();
++
++ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
++ this.getNearbyPlayers().getPlayers(entity.chunkPosition(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
++ if (inRange == null) {
++ return;
++ }
++ final Object[] backingSet = inRange.getRawData();
++ for (int i = 0, len = inRange.size(); i < len; i++) {
++ ++((ServerPlayer)backingSet[i]).mobCounts[index];
++ }
++ }
+ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
+- return -1;
++ return player.mobCounts[mobCategory.ordinal()];
++ // Paper end - Optional per player mob spawns
+ }
+ // Paper end
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+index d2cb358c340bcf7532fd25eccdd33c6945d16de4..2211c054f55e962082eac82aedf5ca625e5b25f2 100644
+--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+@@ -439,7 +439,19 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
+ gameprofilerfiller.popPush("naturalSpawnCount");
+ this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
+ int k = this.distanceManager.getNaturalSpawnChunkCount();
+- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(k, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
++ // Paper start - Optional per player mob spawns
++ int naturalSpawnChunkCount = k;
++ NaturalSpawner.SpawnState spawnercreature_d; // moved down
++ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
++ // re-set mob counts
++ for (ServerPlayer player : this.level.players) {
++ Arrays.fill(player.mobCounts, 0);
++ }
++ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
++ } else {
++ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
++ }
++ // Paper end - Optional per player mob spawns
+ this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
+
+ this.lastSpawnState = spawnercreature_d;
+diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+index ba64e42a58b4b760815f54228ebf7a46fd14734e..6277ec3353c6e552b898b525ce2af76a0c84943d 100644
+--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+@@ -275,6 +275,10 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
+ public boolean queueHealthUpdatePacket;
+ public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
+ // Paper end - cancellable death event
++ // Paper start - Optional per player mob spawns
++ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
++ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
++ // Paper end - Optional per player mob spawns
+
+ // CraftBukkit start
+ public CraftPlayer.TransferCookieConnection transferCookieConnection;
+diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+index ea6533c1ac218aa075da3401807a06fcb7892321..0679636530ca1afd077c43b0fa605a2ac74e0522 100644
+--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+@@ -67,6 +67,12 @@ public final class NaturalSpawner {
+ private NaturalSpawner() {}
+
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
++ // Paper start - Optional per player mob spawns
++ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
++ }
++
++ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
++ // Paper end - Optional per player mob spawns
+ PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
+ Iterator iterator = entities.iterator();
+@@ -99,11 +105,16 @@ public final class NaturalSpawner {
+ spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
+ }
+
+- if (entity instanceof Mob) {
++ if (densityCapper != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
+ densityCapper.addMob(chunk.getPos(), enumcreaturetype);
+ }
+
+ object2intopenhashmap.addTo(enumcreaturetype, 1);
++ // Paper start - Optional per player mob spawns
++ if (countMobs) {
++ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
++ }
++ // Paper end - Optional per player mob spawns
+ });
+ }
+ }
+@@ -138,13 +149,35 @@ public final class NaturalSpawner {
+ continue;
+ }
+
+- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
++ // Paper start - Optional per player mob spawns; only allow spawns upto the limit per chunk and update count afterwards
++ int currEntityCount = info.mobCategoryCounts.getInt(enumcreaturetype);
++ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER;
++ int difference = k1 - currEntityCount;
++
++ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
++ int minDiff = Integer.MAX_VALUE;
++ final com.destroystokyo.paper.util.maplist.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
++ world.chunkSource.chunkMap.getNearbyPlayers().getPlayers(chunk.getPos(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
++ if (inRange != null) {
++ final Object[] backingSet = inRange.getRawData();
++ for (int k = 0, len = inRange.size(); k < len; k++) {
++ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear((net.minecraft.server.level.ServerPlayer)backingSet[k], enumcreaturetype), minDiff);
++ }
++ }
++ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
++ }
++ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) {
++ // Paper end - Optional per player mob spawns
+ // CraftBukkit end
+ Objects.requireNonNull(info);
+ NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
+
+ Objects.requireNonNull(info);
+- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
++ // Paper start - Optional per player mob spawns
++ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
++ difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
++ info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum);
++ // Paper end - Optional per player mob spawns
+ }
+ }
+
+@@ -163,11 +196,17 @@ public final class NaturalSpawner {
+ // Paper end - Add mobcaps commands
+
+ public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
++ // Paper start - Optional per player mob spawns
++ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
++ }
++ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
++ // Paper end - Optional per player mob spawns
+ BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
+
+ if (blockposition.getY() >= world.getMinBuildHeight() + 1) {
+- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
++ return NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
+ }
++ return 0; // Paper - Optional per player mob spawns
+ }
+
+ @VisibleForDebug
+@@ -178,15 +217,21 @@ public final class NaturalSpawner {
+ });
+ }
+
++ // Paper start - Optional per player mob spawns
+ public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
++ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null);
++ }
++ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
++ // Paper end - Optional per player mob spawns
+ StructureManager structuremanager = world.structureManager();
+ ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
+ int i = pos.getY();
+ BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
++ int j = 0; // Paper - Optional per player mob spawns; moved up
+
+ if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
+ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+- int j = 0;
++ //int j = 0; // Paper - Optional per player mob spawns; moved up
+ int k = 0;
+
+ while (k < 3) {
+@@ -228,14 +273,14 @@ public final class NaturalSpawner {
+ // Paper start - PreCreatureSpawnEvent
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
+ if (doSpawning == PreSpawnStatus.ABORT) {
+- return;
++ return j; // Paper - Optional per player mob spawns
+ }
+ if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
+ // Paper end - PreCreatureSpawnEvent
+ Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
+
+ if (entityinsentient == null) {
+- return;
++ return j; // Paper - Optional per player mob spawns
+ }
+
+ entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
+@@ -248,10 +293,15 @@ public final class NaturalSpawner {
+ ++j;
+ ++k1;
+ runner.run(entityinsentient, chunk);
++ // Paper start - Optional per player mob spawns
++ if (trackEntity != null) {
++ trackEntity.accept(entityinsentient);
++ }
++ // Paper end - Optional per player mob spawns
+ }
+ // CraftBukkit end
+- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
+- return;
++ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
++ return j; // Paper - Optional per player mob spawns
+ }
+
+ if (entityinsentient.isMaxGroupSizeReached(k1)) {
+@@ -273,6 +323,7 @@ public final class NaturalSpawner {
+ }
+
+ }
++ return j; // Paper - Optional per player mob spawns
+ }
+
+ private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
+@@ -523,7 +574,7 @@ public final class NaturalSpawner {
+ MobCategory enumcreaturetype = entitytypes.getCategory();
+
+ this.mobCategoryCounts.addTo(enumcreaturetype, 1);
+- this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
++ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); // Paper - Optional per player mob spawns
+ }
+
+ public int getSpawnableChunkCount() {
+@@ -539,6 +590,7 @@ public final class NaturalSpawner {
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit end
+
++ if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper - Optional per player mob spawns
+ return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
+ }
+ }