e035fd7034
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: cc9aa21a SPIGOT-6399, SPIGOT-7344: Clarify collidable behavior for player entities f23325b6 Add API for per-world simulation distances 26e1774e Add API for per-world view distances 0b541e60 Add PlayerLoginEvent#getRealAddress 5f027d2d PR-949: Add Vector#fromJOML() overloads for read-only vector types CraftBukkit Changes: bcf56171a PR-1321: Clean up some stuff which got missed during previous PRs 7f833a2d1 SPIGOT-7462: Players no longer drop XP after dying near a Sculk Catalyst 752aac669 Implement APIs for per world view and simulation distances 57d7ef433 Preserve empty enchantment tags for glow effect 465ec3fb4 Remove connected check on setScoreboard f90ce621e Use one PermissibleBase for all command blocks 5876cca44 SPIGOT-7550: Fix creation of Arrow instances f03fc3aa3 SPIGOT-7549: ServerTickManager#setTickRate incorrect Precondition 9d7f49b01 SPIGOT-7548: Fix wrong spawn location for experience orb and dropped item Spigot Changes: ed9ba9a4 Drop no longer required patch ignoring -o option 86b5dd6a SPIGOT-7546: Fix hardcoded check for outdated client message aa7cde7a Remove obsolete APIs for per world view and simulation distances 6dff577e Remove obsolete patch preserving empty `ench` tags a3bf95b8 Remove obsolete PlayerLoginEvent#getRealAddress 1b02f5d6 Remove obsolete connected check on setScoreboard patch acf717eb Remove obsolete command block PermissibleBase patch 053fa2a9 Remove redundant patch dealing with null tile entities
256 Zeilen
16 KiB
Diff
256 Zeilen
16 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: kickash32 <kickash32@gmail.com>
|
|
Date: Mon, 19 Aug 2019 01:27:58 +0500
|
|
Subject: [PATCH] implement 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 2130f15d92bab1d4e35a92a681ac34cd9c929ea9..1f9efff4ddccf2569fdfe42e6cbc92792643d0ea 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -288,6 +288,29 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
});
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public void updatePlayerMobTypeMap(Entity entity) {
|
|
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
|
+ return;
|
|
+ }
|
|
+ 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(ServerPlayer entityPlayer, net.minecraft.world.entity.MobCategory mobCategory) {
|
|
+ return entityPlayer.mobCounts[mobCategory.ordinal()];
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
|
|
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
|
|
double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 16028723b36c1ca3a3c5d1bf2a7c82d2b42a0be4..24d9a2c37db4bbf2585b33d06f5ea57eb2adf356 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -549,7 +549,19 @@ public class ServerChunkCache extends ChunkSource {
|
|
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 - per player mob spawning
|
|
+ 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
|
|
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 5ef970574ab728dcd118c4d567cb61f0f208b2d7..784be702cbde92a9c81f04fde34ff343056b8ee7 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -253,6 +253,10 @@ public class ServerPlayer extends Player {
|
|
public boolean queueHealthUpdatePacket = false;
|
|
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
|
|
// Paper end
|
|
+ // Paper start - mob spawning rework
|
|
+ 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 - mob spawning rework
|
|
|
|
// 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
|
|
index fe38079d69f3e9987ad5ab077ae09d05017a681a..9df761f5cf043e8d2dffa711c20ab32fe2992331 100644
|
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -70,6 +70,12 @@ public final class NaturalSpawner {
|
|
private NaturalSpawner() {}
|
|
|
|
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
|
|
+ // Paper start - add countMobs parameter
|
|
+ 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
|
|
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
|
|
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
|
|
Iterator iterator = entities.iterator();
|
|
@@ -104,11 +110,16 @@ public final class NaturalSpawner {
|
|
spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
|
|
}
|
|
|
|
- if (entity instanceof Mob) {
|
|
+ if (densityCapper != null && entity instanceof Mob) { // Paper
|
|
densityCapper.addMob(chunk.getPos(), enumcreaturetype);
|
|
}
|
|
|
|
object2intopenhashmap.addTo(enumcreaturetype, 1);
|
|
+ // Paper start
|
|
+ if (countMobs) {
|
|
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
|
|
+ }
|
|
+ // Paper end
|
|
});
|
|
}
|
|
}
|
|
@@ -143,13 +154,35 @@ public final class NaturalSpawner {
|
|
continue;
|
|
}
|
|
|
|
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
|
|
+ // Paper start - 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
|
|
// 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
|
|
+ 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
|
|
}
|
|
}
|
|
|
|
@@ -158,11 +191,17 @@ public final class NaturalSpawner {
|
|
}
|
|
|
|
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
|
+ // Paper start - add parameters and int ret type
|
|
+ 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 - add parameters and int ret type
|
|
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
|
|
}
|
|
+ return 0; // Paper
|
|
}
|
|
|
|
@VisibleForDebug
|
|
@@ -173,15 +212,21 @@ public final class NaturalSpawner {
|
|
});
|
|
}
|
|
|
|
+ // Paper start - add maxSpawns parameter and return spawned mobs
|
|
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 - add maxSpawns parameter and return spawned mobs
|
|
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 - 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 - moved up
|
|
int k = 0;
|
|
|
|
while (k < 3) {
|
|
@@ -223,14 +268,14 @@ public final class NaturalSpawner {
|
|
// Paper start
|
|
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
|
|
if (doSpawning == PreSpawnStatus.ABORT) {
|
|
- return;
|
|
+ return j; // Paper
|
|
}
|
|
if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
|
// Paper end
|
|
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
|
|
|
|
if (entityinsentient == null) {
|
|
- return;
|
|
+ return j; // Paper
|
|
}
|
|
|
|
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
|
|
@@ -243,10 +288,15 @@ public final class NaturalSpawner {
|
|
++j;
|
|
++k1;
|
|
runner.run(entityinsentient, chunk);
|
|
+ // Paper start
|
|
+ if (trackEntity != null) {
|
|
+ trackEntity.accept(entityinsentient);
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
// CraftBukkit end
|
|
- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
|
|
- return;
|
|
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper
|
|
+ return j; // Paper
|
|
}
|
|
|
|
if (entityinsentient.isMaxGroupSizeReached(k1)) {
|
|
@@ -268,6 +318,7 @@ public final class NaturalSpawner {
|
|
}
|
|
|
|
}
|
|
+ return j; // Paper
|
|
}
|
|
|
|
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
|
@@ -567,7 +618,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
|
|
}
|
|
|
|
public int getSpawnableChunkCount() {
|
|
@@ -583,6 +634,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
|
|
return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
|
|
}
|
|
}
|