From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: kickash32 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 56a1d081a28e8b38384cfca732b103462693e322..c1e61a9ccea0c66a664439309593c7f7e6e9e867 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -243,11 +243,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 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 7c9a2eed4441f816723562e0012f918db265912e..cc8638a6dab16ffbdf6951fa10182bd5763df90f 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -541,9 +541,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon private void tickChunks(ProfilerFiller profiler, long timeInhabited) { 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 @@ -572,7 +581,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon profiler.popPush("shuffleSpawningChunks"); // 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 profiler.popPush("tickSpawningChunks"); diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java index 18c0a3de3fd04879f84306e09f5a30502a3d5077..8b0d6efdc6daca8428cdbc8b984441806f7b1a4f 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -406,6 +406,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc public boolean queueHealthUpdatePacket; public @Nullable 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 String displayName; public net.kyori.adventure.text.Component adventure$displayName; // Paper diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java index 3a864c568cd66a680760bb4df2cb020e323e9a9d..c710e08ab48075ce7854e56826adb8f0364b025b 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -66,6 +66,14 @@ public final class NaturalSpawner { public static NaturalSpawner.SpawnState createState( int spawnableChunkCount, Iterable 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 entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs + ) { + // Paper end - Optional per player mob spawns PotentialCalculator potentialCalculator = new PotentialCalculator(); Object2IntOpenHashMap map = new Object2IntOpenHashMap<>(); @@ -87,11 +95,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 }); } } @@ -129,7 +142,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 } @@ -143,8 +156,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(); + org.bukkit.entity.SpawnCategory spawnCategory = org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(mobCategory); + if (org.bukkit.craftbukkit.util.CraftSpawnCategory.isValidForLimits(spawnCategory)) { + limit = level.getWorld().getSpawnLimit(spawnCategory); + } + + // Apply per-player limit + int minDiff = Integer.MAX_VALUE; + final ca.spottedleaf.moonrise.common.list.ReferenceList 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 } } @@ -164,9 +206,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 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 } } @@ -183,6 +232,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 trackEntity + ) { + // Paper end - Optional per player mob spawns StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); int y = pos.getY(); @@ -247,9 +302,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; } @@ -563,7 +623,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() {