2021-06-11 12:02:28 +00:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 01:27:58 +0500
2024-01-21 11:53:04 +00:00
Subject: [PATCH] Optional per player mob spawns
2021-06-14 02:41:44 +00:00
2021-06-11 12:02:28 +00:00
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
2024-03-03 22:05:34 +00:00
index 07a9a11c7fa608e221c0f0e759c483b44de9fdd5..ccf0f7c7feaf47f451cec30ba02bea39ba192b3c 100644
2021-06-11 12:02:28 +00:00
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
2024-01-23 17:01:39 +00:00
@@ -288,9 +288,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2022-09-26 08:02:51 +00:00
});
2021-11-24 09:01:27 +00:00
}
2022-09-26 08:02:51 +00:00
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2024-01-23 17:01:39 +00:00
+ public void updatePlayerMobTypeMap(final Entity entity) {
2022-06-09 08:51:45 +00:00
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
2021-06-11 12:02:28 +00:00
+ return;
+ }
2021-06-14 02:41:44 +00:00
+ int index = entity.getType().getCategory().ordinal();
2021-06-11 12:02:28 +00:00
+
2023-09-24 04:43:10 +00:00
+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
+ this.getNearbyPlayers().getPlayers(entity.chunkPosition(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
2022-01-02 19:06:08 +00:00
+ if (inRange == null) {
+ return;
+ }
2023-09-24 04:43:10 +00:00
+ final Object[] backingSet = inRange.getRawData();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++((ServerPlayer)backingSet[i]).mobCounts[index];
2021-06-11 12:02:28 +00:00
+ }
+ }
+
2024-01-23 17:01:39 +00:00
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
- return -1;
+ return player.mobCounts[mobCategory.ordinal()];
}
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2024-01-23 17:01:39 +00:00
2021-06-11 12:02:28 +00:00
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
2021-06-14 02:41:44 +00:00
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
2021-06-11 12:02:28 +00:00
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
2024-01-24 21:13:08 +00:00
index 20cdfd2bbd5dc71fd37ccedaf3a8d06b45553c9b..059ab637adf1be576fa1fff36a91b6c5f1b5f035 100644
2021-06-11 12:02:28 +00:00
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
2024-01-24 21:13:08 +00:00
@@ -518,7 +518,19 @@ public class ServerChunkCache extends ChunkSource {
2023-12-05 22:32:41 +00:00
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));
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2023-12-05 22:32:41 +00:00
+ 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);
2021-06-11 12:02:28 +00:00
+ }
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2023-12-05 22:32:41 +00:00
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
2021-06-11 12:02:28 +00:00
2023-12-05 22:32:41 +00:00
this.lastSpawnState = spawnercreature_d;
2021-06-11 12:02:28 +00:00
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
2024-04-12 19:14:06 +00:00
index 4a62c2937460dca9d938c40da47529e106503cad..7fb2c0f2576142423cd0e50b811ce4f55795e43d 100644
2021-06-11 12:02:28 +00:00
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
2024-01-23 14:43:48 +00:00
@@ -256,6 +256,10 @@ public class ServerPlayer extends Player {
2024-01-24 10:45:17 +00:00
public boolean queueHealthUpdatePacket;
2021-06-11 12:02:28 +00:00
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
2024-01-24 10:45:17 +00:00
// Paper end - cancellable death event
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2021-07-04 16:30:59 +00:00
+ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
+ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
// CraftBukkit start
public String displayName;
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
2024-02-01 09:15:57 +00:00
index c44c10e15564af6ba0f6d60a1b5f38c6e874a43a..14f4ceb6c0be34d23b24c1695f966145c3aaee96 100644
2021-06-11 12:02:28 +00:00
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
2022-12-07 19:22:28 +00:00
@@ -70,6 +70,12 @@ public final class NaturalSpawner {
2021-06-14 02:41:44 +00:00
private NaturalSpawner() {}
2022-11-12 20:57:41 +00:00
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2022-11-12 20:57:41 +00:00
+ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
2021-06-11 12:02:28 +00:00
+ }
2021-11-24 09:01:27 +00:00
+
2022-11-12 20:57:41 +00:00
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
2021-06-14 02:41:44 +00:00
Iterator iterator = entities.iterator();
2022-12-07 19:22:28 +00:00
@@ -104,11 +110,16 @@ public final class NaturalSpawner {
2023-03-14 19:54:57 +00:00
spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
2022-01-01 02:07:21 +00:00
}
- if (entity instanceof Mob) {
2024-01-21 11:53:04 +00:00
+ if (densityCapper != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
2022-11-12 20:57:41 +00:00
densityCapper.addMob(chunk.getPos(), enumcreaturetype);
2021-06-11 12:02:28 +00:00
}
object2intopenhashmap.addTo(enumcreaturetype, 1);
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
+ if (countMobs) {
2021-06-14 02:41:44 +00:00
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
2021-06-11 12:02:28 +00:00
+ }
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
});
}
}
2023-09-24 04:43:10 +00:00
@@ -143,13 +154,35 @@ public final class NaturalSpawner {
2021-06-11 12:02:28 +00:00
continue;
}
2021-11-24 09:01:27 +00:00
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns; only allow spawns upto the limit per chunk and update count afterwards
2021-06-14 04:29:25 +00:00
+ int currEntityCount = info.mobCategoryCounts.getInt(enumcreaturetype);
2021-06-14 02:41:44 +00:00
+ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER;
2021-06-11 12:02:28 +00:00
+ int difference = k1 - currEntityCount;
+
2022-06-09 08:51:45 +00:00
+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
2021-06-11 12:02:28 +00:00
+ int minDiff = Integer.MAX_VALUE;
2023-09-24 04:43:10 +00:00
+ 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);
2022-01-02 19:06:08 +00:00
+ if (inRange != null) {
2023-09-24 04:43:10 +00:00
+ 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);
2022-01-02 19:06:08 +00:00
+ }
2021-06-11 12:02:28 +00:00
+ }
+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ }
2021-09-17 00:40:34 +00:00
+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) {
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
// CraftBukkit end
2021-06-14 02:41:44 +00:00
Objects.requireNonNull(info);
NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
Objects.requireNonNull(info);
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2021-06-14 02:41:44 +00:00
+ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
2022-06-09 08:51:45 +00:00
+ difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
2021-06-14 04:29:25 +00:00
+ info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum);
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
}
2024-01-23 14:43:48 +00:00
@@ -168,11 +201,17 @@ public final class NaturalSpawner {
// Paper end - Add mobcaps commands
2021-06-11 12:02:28 +00:00
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2022-02-02 20:57:11 +00:00
+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
2021-06-11 12:02:28 +00:00
+ }
2021-06-14 02:41:44 +00:00
+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-14 02:41:44 +00:00
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
2021-06-11 12:02:28 +00:00
2021-06-14 02:41:44 +00:00
if (blockposition.getY() >= world.getMinBuildHeight() + 1) {
2021-11-24 09:01:27 +00:00
- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
2024-01-21 11:53:04 +00:00
+ return NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
2024-01-21 11:53:04 +00:00
+ return 0; // Paper - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
2021-06-14 02:41:44 +00:00
@VisibleForDebug
2024-01-23 14:43:48 +00:00
@@ -183,15 +222,21 @@ public final class NaturalSpawner {
2021-06-14 02:41:44 +00:00
});
}
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
2022-02-02 20:57:11 +00:00
+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null);
2021-06-11 12:02:28 +00:00
+ }
2021-06-14 02:41:44 +00:00
+ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2022-06-08 04:06:41 +00:00
StructureManager structuremanager = world.structureManager();
2021-06-14 02:41:44 +00:00
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
int i = pos.getY();
2021-12-05 23:32:02 +00:00
BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
2024-01-21 11:53:04 +00:00
+ int j = 0; // Paper - Optional per player mob spawns; moved up
2021-06-14 02:41:44 +00:00
if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
2021-06-11 12:02:28 +00:00
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
- int j = 0;
2024-01-21 11:53:04 +00:00
+ //int j = 0; // Paper - Optional per player mob spawns; moved up
2021-06-11 12:02:28 +00:00
int k = 0;
while (k < 3) {
2024-01-23 14:43:48 +00:00
@@ -233,14 +278,14 @@ public final class NaturalSpawner {
2024-01-22 17:04:55 +00:00
// Paper start - PreCreatureSpawnEvent
2023-08-21 07:44:47 +00:00
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
if (doSpawning == PreSpawnStatus.ABORT) {
2021-06-14 06:45:29 +00:00
- return;
2024-01-21 11:53:04 +00:00
+ return j; // Paper - Optional per player mob spawns
2021-06-14 06:45:29 +00:00
}
2023-08-21 07:44:47 +00:00
if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
2024-01-22 17:04:55 +00:00
// Paper end - PreCreatureSpawnEvent
2021-06-14 02:41:44 +00:00
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
2021-06-11 12:02:28 +00:00
if (entityinsentient == null) {
- return;
2024-01-21 11:53:04 +00:00
+ return j; // Paper - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
2021-06-14 02:41:44 +00:00
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
2024-01-23 14:43:48 +00:00
@@ -253,10 +298,15 @@ public final class NaturalSpawner {
2021-06-14 02:41:44 +00:00
++j;
2021-06-11 12:02:28 +00:00
++k1;
2021-06-14 02:41:44 +00:00
runner.run(entityinsentient, chunk);
2024-01-21 11:53:04 +00:00
+ // Paper start - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
+ if (trackEntity != null) {
+ trackEntity.accept(entityinsentient);
+ }
2024-01-21 11:53:04 +00:00
+ // Paper end - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
// CraftBukkit end
- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
- return;
2024-01-21 11:53:04 +00:00
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
+ return j; // Paper - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
if (entityinsentient.isMaxGroupSizeReached(k1)) {
2024-01-23 14:43:48 +00:00
@@ -278,6 +328,7 @@ public final class NaturalSpawner {
2021-06-11 12:02:28 +00:00
}
}
2024-01-21 11:53:04 +00:00
+ return j; // Paper - Optional per player mob spawns
2021-06-11 12:02:28 +00:00
}
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
2024-02-01 09:15:57 +00:00
@@ -573,7 +624,7 @@ public final class NaturalSpawner {
2022-01-01 02:07:21 +00:00
MobCategory enumcreaturetype = entitytypes.getCategory();
this.mobCategoryCounts.addTo(enumcreaturetype, 1);
- this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
2024-01-21 11:53:04 +00:00
+ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); // Paper - Optional per player mob spawns
2022-01-01 02:07:21 +00:00
}
public int getSpawnableChunkCount() {
2024-02-01 09:15:57 +00:00
@@ -589,6 +640,7 @@ public final class NaturalSpawner {
2022-01-01 02:07:21 +00:00
int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
// CraftBukkit end
2024-01-21 11:53:04 +00:00
+ if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper - Optional per player mob spawns
2022-01-01 02:07:21 +00:00
return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
}
}