aboutsummaryrefslogtreecommitdiffhomepage
path: root/paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch
diff options
context:
space:
mode:
Diffstat (limited to 'paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch')
-rw-r--r--paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch236
1 files changed, 236 insertions, 0 deletions
diff --git a/paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch b/paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch
new file mode 100644
index 0000000000..500d8debd1
--- /dev/null
+++ b/paper-server/patches/features/0027-Optional-per-player-mob-spawns.patch
@@ -0,0 +1,236 @@
+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/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
+index ff6503bf8eb88d1264c3d848a89d0255b4b3ae68..9eed24939fc09f00a9dbce1be2ab9c34d024fd29 100644
+--- a/net/minecraft/server/level/ChunkMap.java
++++ b/net/minecraft/server/level/ChunkMap.java
+@@ -236,11 +236,29 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ // Paper - rewrite chunk system
+ }
+
+- // Paper start
+- public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
+- return -1;
++ // 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 ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> inRange =
++ this.level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
++ if (inRange == null) {
++ return;
++ }
++
++ final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
++ for (int i = 0, len = inRange.size(); i < len; i++) {
++ ++(backingSet[i].mobCounts[index]);
++ }
+ }
+- // Paper end
++
++ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
++ return player.mobCounts[mobCategory.ordinal()];
++ }
++ // Paper end - Optional per player mob spawns
+
+ protected ChunkGenerator generator() {
+ return this.worldGenContext.generator();
+diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
+index 87d4291a3944f706a694536da6de0f28c548ab8d..5576bf1d1d70ab7a010653d3207909b5de867e70 100644
+--- a/net/minecraft/server/level/ServerChunkCache.java
++++ b/net/minecraft/server/level/ServerChunkCache.java
+@@ -517,7 +517,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
+ profilerFiller.popPush("shuffleChunks");
+ // Paper start - chunk tick iteration optimisation
+ this.shuffleRandom.setSeed(this.level.random.nextLong());
+- Util.shuffle(list, this.shuffleRandom);
++ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
+ // Paper end - chunk tick iteration optimisation
+ this.tickChunks(profilerFiller, l, list);
+ profilerFiller.pop();
+@@ -571,9 +571,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
+ private void tickChunks(ProfilerFiller profiler, long timeInhabited, List<LevelChunk> chunks) {
+ profiler.popPush("naturalSpawnCount");
+ int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount();
+- NaturalSpawner.SpawnState spawnState = NaturalSpawner.createState(
+- naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)
+- );
++ // Paper start - Optional per player mob spawns
++ NaturalSpawner.SpawnState spawnState;
++ 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);
++ }
++ spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
++ } else {
++ spawnState = 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.lastSpawnState = spawnState;
+ profiler.popPush("spawnAndTick");
+ boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
+diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
+index 0a7e5106a1d39150326e7c323030df5d32ecef1e..a63702dd7e86fc8b9f78c2ae23e23b65b6b2ee24 100644
+--- a/net/minecraft/server/level/ServerPlayer.java
++++ b/net/minecraft/server/level/ServerPlayer.java
+@@ -368,6 +368,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
+ 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 end - Optional per player mob spawns
+ // CraftBukkit start
+ public org.bukkit.craftbukkit.entity.CraftPlayer.TransferCookieConnection transferCookieConnection;
+ public String displayName;
+diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
+index 913ea92ace9d610c25bf28f703a3b227044aea63..ef8bacbbb43a9b80281a313ca43b7efff5a93e03 100644
+--- a/net/minecraft/world/level/NaturalSpawner.java
++++ b/net/minecraft/world/level/NaturalSpawner.java
+@@ -72,6 +72,14 @@ public final class NaturalSpawner {
+ public static NaturalSpawner.SpawnState createState(
+ int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator
+ ) {
++ // Paper start - Optional per player mob spawns
++ return createState(spawnableChunkCount, entities, chunkGetter, calculator, false);
++ }
++
++ public static NaturalSpawner.SpawnState createState(
++ int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
++ ) {
++ // Paper end - Optional per player mob spawns
+ PotentialCalculator potentialCalculator = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
+
+@@ -93,11 +101,16 @@ public final class NaturalSpawner {
+ potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
+ }
+
+- if (entity instanceof Mob) {
++ if (calculator != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
+ calculator.addMob(chunk.getPos(), category);
+ }
+
+ map.addTo(category, 1);
++ // Paper start - Optional per player mob spawns
++ if (countMobs) {
++ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
++ }
++ // Paper end - Optional per player mob spawns
+ });
+ }
+ }
+@@ -135,7 +148,7 @@ public final class NaturalSpawner {
+ if ((spawnFriendlies || !mobCategory.isFriendly())
+ && (spawnEnemies || mobCategory.isFriendly())
+ && (spawnPassives || !mobCategory.isPersistent())
+- && spawnState.canSpawnForCategoryGlobal(mobCategory, limit)) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
++ && (level.paperConfig().entities.spawning.perPlayerMobSpawns || spawnState.canSpawnForCategoryGlobal(mobCategory, limit))) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
+ list.add(mobCategory);
+ // CraftBukkit end
+ }
+@@ -149,8 +162,37 @@ public final class NaturalSpawner {
+ profilerFiller.push("spawner");
+
+ for (MobCategory mobCategory : categories) {
+- if (spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos())) {
+- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn);
++ // Paper start - Optional per player mob spawns
++ final boolean canSpawn;
++ int maxSpawns = Integer.MAX_VALUE;
++ if (level.paperConfig().entities.spawning.perPlayerMobSpawns) {
++ // Copied from getFilteredSpawningCategories
++ int limit = mobCategory.getMaxInstancesPerChunk();
++ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory);
++ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
++ limit = level.getWorld().getSpawnLimit(spawnCategory);
++ }
++
++ // Apply per-player limit
++ int minDiff = Integer.MAX_VALUE;
++ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
++ level.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
++ if (inRange != null) {
++ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
++ for (int k = 0, len = inRange.size(); k < len; k++) {
++ minDiff = Math.min(limit - level.getChunkSource().chunkMap.getMobCountNear(backingSet[k], mobCategory), minDiff);
++ }
++ }
++
++ maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
++ canSpawn = maxSpawns > 0;
++ } 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 end - Optional per player mob spawns
+ }
+ }
+
+@@ -170,9 +212,16 @@ public final class NaturalSpawner {
+ public static void spawnCategoryForChunk(
+ MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback
+ ) {
++ // Paper start - Optional per player mob spawns
++ spawnCategoryForChunk(category, level, chunk, filter, callback, Integer.MAX_VALUE, null);
++ }
++ public static void spawnCategoryForChunk(
++ MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity
++ ) {
++ // 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);
++ spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
+ }
+ }
+
+@@ -189,6 +238,12 @@ public final class NaturalSpawner {
+ NaturalSpawner.SpawnPredicate filter,
+ NaturalSpawner.AfterSpawnCallback callback
+ ) {
++ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, Integer.MAX_VALUE, null);
++ }
++ 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 end - Optional per player mob spawns
+ StructureManager structureManager = level.structureManager();
+ ChunkGenerator generator = level.getChunkSource().getGenerator();
+ int y = pos.getY();
+@@ -252,9 +307,14 @@ public final class NaturalSpawner {
+ ++i;
+ ++i3;
+ callback.run(mobForSpawn, chunk);
++ // Paper start - Optional per player mob spawns
++ if (trackEntity != null) {
++ trackEntity.accept(mobForSpawn);
++ }
++ // Paper end - Optional per player mob spawns
+ }
+ // CraftBukkit end
+- if (i >= mobForSpawn.getMaxSpawnClusterSize()) {
++ if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns
+ return;
+ }
+
+@@ -565,7 +625,7 @@ public final class NaturalSpawner {
+ this.spawnPotential.addCharge(blockPos, d);
+ MobCategory category = type.getCategory();
+ this.mobCategoryCounts.addTo(category, 1);
+- this.localMobCapCalculator.addMob(new ChunkPos(blockPos), category);
++ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockPos), category); // Paper - Optional per player mob spawns
+ }
+
+ public int getSpawnableChunkCount() {