diff options
4 files changed, 235 insertions, 231 deletions
diff --git a/feature-patches/1066-Incremental-chunk-and-player-saving.patch b/feature-patches/1066-Incremental-chunk-and-player-saving.patch index 9e117f351b..17ee1796ca 100644 --- a/feature-patches/1066-Incremental-chunk-and-player-saving.patch +++ b/feature-patches/1066-Incremental-chunk-and-player-saving.patch @@ -4,23 +4,23 @@ Date: Sun, 9 Jun 2019 03:53:22 +0100 Subject: [PATCH] Incremental chunk and player saving -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index f4fba4e2d12c7ab4b4eb9858cd738a9678a2d203..a0a75c84379432ccc525ab22d476c358c77f663b 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -862,7 +862,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + boolean var4; try { this.isSaving = true; - this.getPlayerList().saveAll(); + this.getPlayerList().saveAll(); // Paper - Incremental chunk and player saving; diff on change - flag3 = this.saveAllChunks(suppressLogs, flush, force); + var4 = this.saveAllChunks(suppressLog, flush, forced); } finally { this.isSaving = false; -@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa +@@ -1409,9 +1409,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa } - --this.ticksUntilAutosave; + this.ticksUntilAutosave--; - if (this.autosavePeriod > 0 && this.ticksUntilAutosave <= 0) { // CraftBukkit - this.autoSave(); + // Paper start - Incremental chunk and player saving @@ -47,22 +47,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + profiler.pop(); + // Paper end - Incremental chunk and player saving - ProfilerFiller gameprofilerfiller = Profiler.get(); - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + ProfilerFiller profilerFiller = Profiler.get(); + this.runAllTasks(); // Paper - move runAllTasks() into full server tick (previously for timings) +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index c38eda42b33cfa4792625f40ebde6f30e591119b..c8129f0d8218daff9123f1ad2d8ca321a02e1c7e 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1007,6 +1007,28 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); } + // Paper start - Incremental chunk and player saving + public void saveIncrementally(boolean doFull) { -+ ServerChunkCache chunkproviderserver = this.getChunkSource(); -+ + if (doFull) { -+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); ++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); + } + + if (doFull) { @@ -72,8 +70,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Copied from save() + // CraftBukkit start - moved from MinecraftServer.saveChunks + if (doFull) { // Paper -+ ServerLevel worldserver1 = this; -+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings()); ++ ServerLevel serverLevel1 = this; ++ this.serverLevelData.setWorldBorder(serverLevel1.getWorldBorder().createSettings()); + this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess())); + this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); + } @@ -81,34 +79,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end - Incremental chunk and player saving + - public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) { - // Paper start - add close param - this.save(progressListener, flush, savingDisabled, false); -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -0,0 +0,0 @@ import org.bukkit.inventory.MainHand; - public class ServerPlayer extends net.minecraft.world.entity.player.Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system + public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { + ServerChunkCache chunkSource = this.getChunkSource(); + if (!skipSave) { +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 92bd46cca1956f327fb0b407e988d68782f441a4..0f00db82e85c9e510c2e4fe4065291971c408dad 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -180,6 +180,7 @@ import org.slf4j.Logger; + public class ServerPlayer extends Player { private static final Logger LOGGER = LogUtils.getLogger(); + public long lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; private static final int FLY_STAT_RECORDING_SPEED = 25; -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -0,0 +0,0 @@ public abstract class PlayerList { +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index bafeeab3edbc73f6f86474e18ab4a3d96ce17157..aaa6b8eee7b34fe6efa76f1fe997dcece827d5dd 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -483,6 +483,7 @@ public abstract class PlayerList { protected void save(ServerPlayer player) { if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit + player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving this.playerIo.save(player); - ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit - -@@ -0,0 +0,0 @@ public abstract class PlayerList { + ServerStatsCounter serverStatsCounter = player.getStats(); // CraftBukkit + if (serverStatsCounter != null) { +@@ -1070,9 +1071,23 @@ public abstract class PlayerList { } public void saveAll() { @@ -116,18 +114,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.saveAll(-1); + } + -+ public void saveAll(int interval) { ++ public void saveAll(final int interval) { io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main + int numSaved = 0; -+ long now = MinecraftServer.currentTick; - for (int i = 0; i < this.players.size(); ++i) { -- this.save((ServerPlayer) this.players.get(i)); ++ final long now = MinecraftServer.currentTick; + for (int i = 0; i < this.players.size(); i++) { +- this.save(this.players.get(i)); + final ServerPlayer player = this.players.get(i); + if (interval == -1 || now - player.lastSave >= interval) { + this.save(player); -+ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; } ++ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { ++ break; ++ } + } + // Paper end - Incremental chunk and player saving } - return null; }); // Paper - ensure main + } diff --git a/feature-patches/1071-Optional-per-player-mob-spawns.patch b/feature-patches/1071-Optional-per-player-mob-spawns.patch index dda7294b2a..2604d7bb97 100644 --- a/feature-patches/1071-Optional-per-player-mob-spawns.patch +++ b/feature-patches/1071-Optional-per-player-mob-spawns.patch @@ -4,229 +4,233 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java +index 809f3fe1285e347f18709c2368923fcc8f953ded..6c5b2eb411fb60babbb0c74d5c075696ef70b38d 100644 +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -237,11 +237,29 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.chunksToEagerlySave.add(chunkPos.toLong()); } - // Paper start +- // 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 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]); + } -+ } - 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 +- // 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 -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - gameprofilerfiller.popPush("shuffleChunks"); - // Paper start - chunk tick iteration optimisation - this.shuffleRandom.setSeed(this.level.random.nextLong()); -- Util.shuffle(list, this.shuffleRandom); + protected ChunkGenerator generator() { + return this.worldGenContext.generator(); +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 2f49dbc919f7f5eea9abce6106723c72f5ae45fb..078f208e104a652ce48458150389d19ede6808ef 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -435,7 +435,7 @@ public class ServerChunkCache extends ChunkSource { + profilerFiller.push("filteringTickingChunks"); + this.collectTickingChunks(list); + profilerFiller.popPush("shuffleChunks"); +- Util.shuffle(list, this.level.random); + 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(gameprofilerfiller, j, list); - gameprofilerfiller.pop(); -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - private void tickChunks(ProfilerFiller profiler, long timeDelta, List<LevelChunk> chunks) { + this.tickChunks(profilerFiller, l, list); + profilerFiller.pop(); + } finally { +@@ -474,9 +474,18 @@ public class ServerChunkCache extends ChunkSource { + private void tickChunks(ProfilerFiller profiler, long timeInhabited, List<LevelChunk> chunks) { profiler.popPush("naturalSpawnCount"); - int j = this.distanceManager.getNaturalSpawnChunkCount(); -- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(j, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)); + 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 -+ final int naturalSpawnChunkCount = j; -+ NaturalSpawner.SpawnState spawnercreature_d; // moved down ++ 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); + } -+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); ++ spawnState = 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); ++ 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 = spawnercreature_d; + this.lastSpawnState = spawnState; profiler.popPush("spawnAndTick"); -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple + 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 0f00db82e85c9e510c2e4fe4065291971c408dad..dab58457ed02d3f8153c07de101262b1a0182d71 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -368,6 +368,10 @@ public class ServerPlayer extends Player { 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 ++ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; + // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -0,0 +0,0 @@ public final class NaturalSpawner { - private NaturalSpawner() {} - - public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) { + 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 6e6e028621ccc4597b2c24f54f53cb7f3de603e2..14e99450a8522b79e4c3805bd91439a950bc8f99 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(spawningChunkCount, entities, chunkSource, densityCapper, false); ++ return createState(spawnableChunkCount, entities, chunkGetter, calculator, false); + } + -+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) { ++ 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 spawnercreatureprobabilities = new PotentialCalculator(); - Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap(); - Iterator iterator = entities.iterator(); -@@ -0,0 +0,0 @@ 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 - }); - } - } -@@ -0,0 +0,0 @@ public final class NaturalSpawner { - continue; + 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 + }); + } } - -- if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) { -+ if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && (worldserver.paperConfig().entities.spawning.perPlayerMobSpawns || spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit))) { // Paper - Optional per player mob spawns; remove global check, check later during the local one +@@ -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 - list.add(enumcreaturetype); } -@@ -0,0 +0,0 @@ public final class NaturalSpawner { - while (iterator.hasNext()) { - MobCategory enumcreaturetype = (MobCategory) iterator.next(); +@@ -149,8 +162,37 @@ public final class NaturalSpawner { + profilerFiller.push("spawner"); -- if (info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos())) { + 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 (world.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ if (level.paperConfig().entities.spawning.perPlayerMobSpawns) { + // Copied from getFilteredSpawningCategories -+ int limit = enumcreaturetype.getMaxInstancesPerChunk(); -+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype); ++ int limit = mobCategory.getMaxInstancesPerChunk(); ++ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory); + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { -+ limit = world.getWorld().getSpawnLimit(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 = -+ world.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); ++ 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 - world.getChunkSource().chunkMap.getMobCountNear(backingSet[k], enumcreaturetype), minDiff); ++ minDiff = Math.min(limit - level.getChunkSource().chunkMap.getMobCountNear(backingSet[k], mobCategory), minDiff); + } + } + + maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; + canSpawn = maxSpawns > 0; + } else { -+ canSpawn = info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos()); ++ canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos()); + } + if (canSpawn) { -+ // Paper end - Optional per player mob spawns - 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 -+ NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, -+ maxSpawns, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); ++ 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 } } -@@ -0,0 +0,0 @@ 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) { +@@ -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(group, world, chunk, checker, runner, Integer.MAX_VALUE, null); ++ spawnCategoryForChunk(category, level, chunk, filter, callback, Integer.MAX_VALUE, null); + } -+ public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) { ++ 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 blockposition = NaturalSpawner.getRandomPosWithin(world, chunk); - - if (blockposition.getY() >= world.getMinY() + 1) { -- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner); -+ NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - 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 } } -@@ -0,0 +0,0 @@ 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); +@@ -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 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(); -@@ -0,0 +0,0 @@ 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()) { -+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns - return; - } - -@@ -0,0 +0,0 @@ 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 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() { diff --git a/feature-patches/1072-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/feature-patches/1072-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch index 46e601aaff..d94b29471c 100644 --- a/feature-patches/1072-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch +++ b/feature-patches/1072-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch @@ -5,14 +5,14 @@ Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ++(backingSet[i].mobCounts[index]); +diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java +index 6c5b2eb411fb60babbb0c74d5c075696ef70b38d..cf439285e4ba9babda228c36aa81dfc49db2c22a 100644 +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -256,8 +256,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } + + // Paper start - per player mob count backoff + public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) { + if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { @@ -33,14 +33,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) { - return player.mobCounts[mobCategory.ordinal()]; + return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff - // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + // Paper end - Optional per player mob spawns + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 078f208e104a652ce48458150389d19ede6808ef..aa141c00a41d49daee8e4ab7be70ce4e4767b105 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -479,7 +479,17 @@ public class ServerChunkCache extends ChunkSource { 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) { @@ -57,33 +57,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end - per player mob spawning backoff } - spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); + spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); } else { -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index dab58457ed02d3f8153c07de101262b1a0182d71..2d20f42fbcfb67845323d994843d7b977aa867e0 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -372,6 +372,7 @@ public class ServerPlayer extends Player { public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length; - public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper + public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper end - Optional per player mob spawns + public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff - // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -0,0 +0,0 @@ public final class NaturalSpawner { + 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 5e82a8fdaec5a6750040ebb687aa35bba4dcc2ba..23f7fb22906e49babc7784f4b3d1f8ea8e187b1d 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -285,6 +285,11 @@ public final class NaturalSpawner { - // Paper start - PreCreatureSpawnEvent - PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); -+ // Paper start - per player mob count backoff -+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) { -+ world.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(blockposition_mutableblockposition.getX() >> 4, blockposition_mutableblockposition.getZ() >> 4, group); -+ } -+ // Paper end - per player mob count backoff - if (doSpawning == PreSpawnStatus.ABORT) { - return; - } + // Paper start - PreCreatureSpawnEvent + PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2); ++ // Paper start - per player mob count backoff ++ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) { ++ level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category); ++ } ++ // Paper end - per player mob count backoff + if (doSpawning == PreSpawnStatus.ABORT) { + return; + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch index d5a3311160..89849d7d70 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch @@ -35,9 +35,9 @@ + // CraftBukkit start - add server public static List<MobCategory> getFilteredSpawningCategories( - NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives -+ NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives, ServerLevel worldserver ++ NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives, ServerLevel level ) { -+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate ++ LevelData worlddata = level.getLevelData(); // CraftBukkit - Other mob type spawn tick rate + // CraftBukkit end List<MobCategory> list = new ArrayList<>(SPAWNING_CATEGORIES.length); @@ -47,8 +47,8 @@ + int limit = mobCategory.getMaxInstancesPerChunk(); + SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory); + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { -+ spawnThisTick = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0; -+ limit = worldserver.getWorld().getSpawnLimit(spawnCategory); ++ spawnThisTick = level.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(spawnCategory) == 0; ++ limit = level.getWorld().getSpawnLimit(spawnCategory); + } + + if (!spawnThisTick || limit == 0) { |