From 7007f67ebb54d312283c2a1c46cf79ab3c6525f3 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sun, 13 Jun 2021 23:45:29 -0700 Subject: [PATCH] patches --- ...k-Priority-Urgency-System-for-Chunks.patch | 1352 ----------------- ...mprove-Chunk-Status-Transition-Speed.patch | 99 -- ...ookup-for-Treasure-Maps-Fixes-lag-fr.patch | 35 - ...ix-villager-trading-demand-MC-163962.patch | 0 .../Maps-shouldn-t-load-chunks.patch | 0 .../Optimize-Bit-Operations-by-inlining.patch | 95 +- ...-packets-to-nearby-locations-sounds-.patch | 2 +- ...ookup-for-Treasure-Maps-Fixes-lag-fr.patch | 20 + ...ement-optional-per-player-mob-spawns.patch | 8 + 9 files changed, 73 insertions(+), 1538 deletions(-) delete mode 100644 patches/server-remapped/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch delete mode 100644 patches/server-remapped/Improve-Chunk-Status-Transition-Speed.patch delete mode 100644 patches/server-remapped/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch rename patches/{server-remapped => server}/Fix-villager-trading-demand-MC-163962.patch (100%) rename patches/{server-remapped => server}/Maps-shouldn-t-load-chunks.patch (100%) rename patches/{server-remapped => server}/Optimize-Bit-Operations-by-inlining.patch (74%) rename patches/{server-remapped => server}/Optimize-sending-packets-to-nearby-locations-sounds-.patch (96%) create mode 100644 patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch diff --git a/patches/server-remapped/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/patches/server-remapped/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch deleted file mode 100644 index 54354fb02d..0000000000 --- a/patches/server-remapped/Implement-Chunk-Priority-Urgency-System-for-Chunks.patch +++ /dev/null @@ -1,1352 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sat, 11 Apr 2020 03:56:07 -0400 -Subject: [PATCH] Implement Chunk Priority / Urgency System for Chunks - -Mark chunks that are blocking main thread for world generation as urgent - -Implements a general priority system so that chunks that are sorted in -the generator queues can prioritize certain chunks over another. - -Urgent chunks will jump to the front of the line, ensuring that a -sync chunk load on an ungenerated chunk does not lag the server for -a long period of time if the servers generator queues are filled with -lots of chunks already. - -This massively reduces the lag spikes from sync chunk gens. - -Then we further prioritize loading order so nearby chunks have higher -priority than distant chunks, reducing the pressure a high no tick -view distance holds on you. - -Chunks in front of the player have higher priority, to help with -fast traveling players keep up with their movement. - -diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -@@ -0,0 +0,0 @@ public final class ChunkTaskManager { - } - - static void dumpChunkInfo(Set seenChunks, ChunkHolder chunkHolder, int x, int z) { -- dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 1); -+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 4); - } - - static void dumpChunkInfo(Set seenChunks, ChunkHolder chunkHolder, int x, int z, int indent, int maxDepth) { -@@ -0,0 +0,0 @@ public final class ChunkTaskManager { - PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getStatus().toString())); - PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Ticket Status - " + ChunkHolder.getStatus(chunkHolder.getTicketLevel())); - PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder Status - " + ((holderStatus == null) ? "null" : holderStatus.toString())); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder Priority - " + chunkHolder.getCurrentPriority()); -+ -+ if (!chunkHolder.neighbors.isEmpty()) { -+ if (indent >= maxDepth) { -+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Neighbors: (Can't show, too deeply nested)"); -+ return; -+ } -+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Neighbors: "); -+ for (ChunkHolder neighbor : chunkHolder.neighbors.keySet()) { -+ ChunkStatus status = neighbor.getChunkHolderStatus(); -+ if (status != null && status.isAtLeastStatus(ChunkHolder.getStatus(neighbor.getTicketLevel()))) { -+ continue; -+ } -+ int nx = neighbor.pos.x; -+ int nz = neighbor.pos.z; -+ if (seenChunks.contains(neighbor)) { -+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + " (CIRCULAR)"); -+ continue; -+ } -+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + " " + nx + "," + nz + " in " + chunkHolder.getWorld().getWorld().getName() + ":"); -+ dumpChunkInfo(seenChunks, neighbor, nx, nz, indent + 1, maxDepth); -+ } -+ } -+ - } - } - -diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/MCUtil.java -+++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -0,0 +0,0 @@ public final class MCUtil { - - // sorting by coordinate makes the log easier to read - allChunks.sort((ChunkHolder v1, ChunkHolder v2) -> { -- if (v1.location.x != v2.location.x) { -- return Integer.compare(v1.location.x, v2.location.x); -+ if (v1.pos.x != v2.pos.x) { -+ return Integer.compare(v1.pos.x, v2.pos.x); - } -- return Integer.compare(v1.location.z, v2.location.z); -+ return Integer.compare(v1.pos.z, v2.pos.z); - }); - - worldData.addProperty("name", world.getWorld().getName()); -@@ -0,0 +0,0 @@ public final class MCUtil { - for (ChunkHolder playerChunk : allChunks) { - JsonObject chunkData = new JsonObject(); - -- Set> tickets = chunkMapDistance.tickets.get(playerChunk.pos.pair()); -+ Set> tickets = chunkMapDistance.tickets.get(playerChunk.pos.toLong()); - ChunkStatus status = getChunkStatus(playerChunk); - -- chunkData.addProperty("x", playerChunk.location.x); -- chunkData.addProperty("z", playerChunk.location.z); -+ chunkData.addProperty("x", playerChunk.pos.x); -+ chunkData.addProperty("z", playerChunk.pos.z); - chunkData.addProperty("ticket-level", playerChunk.getTicketLevel()); -+ chunkData.addProperty("priority", playerChunk.getCurrentPriority()); - chunkData.addProperty("state", ChunkHolder.getFullChunkStatus(playerChunk.getTicketLevel()).toString()); -- chunkData.addProperty("queued-for-unload", chunkMap.toDrop.contains(playerChunk.pos.pair())); -+ chunkData.addProperty("queued-for-unload", chunkMap.toDrop.contains(playerChunk.pos.toLong())); - chunkData.addProperty("status", status == null ? "unloaded" : status.toString()); - - JsonArray ticketsData = new JsonArray(); -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -0,0 +0,0 @@ - package net.minecraft.server.level; - - import com.mojang.datafixers.util.Either; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper - import it.unimi.dsi.fastutil.shorts.ShortArraySet; - import it.unimi.dsi.fastutil.shorts.ShortSet; - import java.util.List; -@@ -0,0 +0,0 @@ import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; - import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; - import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; - import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; -+import net.minecraft.server.MCUtil; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.Level; - import net.minecraft.world.level.LightLayer; -@@ -0,0 +0,0 @@ public class ChunkHolder { - private CompletableFuture chunkToSave; - public int oldTicketLevel; - private int ticketLevel; -- private int queueLevel; -- final ChunkPos pos; // Paper - private -> package -+ volatile int queueLevel; public final int getCurrentPriority() { return queueLevel; } // Paper - OBFHELPER - make volatile since this is concurrently accessed -+ public final ChunkPos pos; // Paper - private -> public - private boolean hasChangedSections; - private final ShortSet[] changedBlocksPerSection; - private int blockChangedLightSectionFilter; -@@ -0,0 +0,0 @@ public class ChunkHolder { - private boolean resendLight; - - private final ChunkMap chunkMap; // Paper -+ public ServerLevel getWorld() { return chunkMap.level; } // Paper - - long lastAutoSaveTime; // Paper - incremental autosave - long inactiveTimeStart; // Paper - incremental autosave -@@ -0,0 +0,0 @@ public class ChunkHolder { - return null; - } - // Paper end - no-tick view distance -+ // Paper start - Chunk gen/load priority system -+ volatile int neighborPriority = -1; -+ volatile int priorityBoost = 0; -+ public final java.util.concurrent.ConcurrentHashMap neighbors = new java.util.concurrent.ConcurrentHashMap<>(); -+ public final Long2ObjectOpenHashMap neighborPriorities = new Long2ObjectOpenHashMap<>(); -+ -+ private int getDemandedPriority() { -+ int priority = neighborPriority; // if we have a neighbor priority, use it -+ int myPriority = getMyPriority(); -+ -+ if (priority == -1 || (ticketLevel <= 33 && priority > myPriority)) { -+ priority = myPriority; -+ } -+ -+ return Math.max(1, Math.min(Math.max(ticketLevel, ChunkMap.MAX_CHUNK_DISTANCE), priority)); -+ } -+ -+ private int getMyPriority() { -+ if (priorityBoost == DistanceManager.URGENT_PRIORITY) { -+ return 2; // Urgent - ticket level isn't always 31 so 33-30 = 3, but allow 1 more tasks to go below this for dependents -+ } -+ return ticketLevel - priorityBoost; -+ } -+ -+ private int getNeighborsPriority() { -+ return (neighborPriorities.isEmpty() ? getMyPriority() : getDemandedPriority()) + 1; -+ } -+ -+ public void onNeighborRequest(ChunkHolder neighbor, ChunkStatus status) { -+ neighbor.setNeighborPriority(this, getNeighborsPriority()); -+ this.neighbors.compute(neighbor, (playerChunk, currentWantedStatus) -> { -+ if (currentWantedStatus == null || !currentWantedStatus.isAtLeastStatus(status)) { -+ //System.out.println(this + " request " + neighbor + " at " + status + " currently " + currentWantedStatus); -+ return status; -+ } else { -+ //System.out.println(this + " requested " + neighbor + " at " + status + " but thats lower than other wanted status " + currentWantedStatus); -+ return currentWantedStatus; -+ } -+ }); -+ -+ } -+ -+ public void onNeighborDone(ChunkHolder neighbor, ChunkStatus chunkstatus, ChunkAccess chunk) { -+ this.neighbors.compute(neighbor, (playerChunk, wantedStatus) -> { -+ if (wantedStatus != null && chunkstatus.isAtLeastStatus(wantedStatus)) { -+ //System.out.println(this + " neighbor done at " + neighbor + " for status " + chunkstatus + " wanted " + wantedStatus); -+ neighbor.removeNeighborPriority(this); -+ return null; -+ } else { -+ //System.out.println(this + " neighbor finished our previous request at " + neighbor + " for status " + chunkstatus + " but we now want instead " + wantedStatus); -+ return wantedStatus; -+ } -+ }); -+ } -+ -+ private void removeNeighborPriority(ChunkHolder requester) { -+ synchronized (neighborPriorities) { -+ neighborPriorities.remove(requester.pos.toLong()); -+ recalcNeighborPriority(); -+ } -+ checkPriority(); -+ } -+ -+ -+ private void setNeighborPriority(ChunkHolder requester, int priority) { -+ synchronized (neighborPriorities) { -+ neighborPriorities.put(requester.pos.toLong(), Integer.valueOf(priority)); -+ recalcNeighborPriority(); -+ } -+ checkPriority(); -+ } -+ -+ private void recalcNeighborPriority() { -+ neighborPriority = -1; -+ if (!neighborPriorities.isEmpty()) { -+ synchronized (neighborPriorities) { -+ for (Integer neighbor : neighborPriorities.values()) { -+ if (neighbor < neighborPriority || neighborPriority == -1) { -+ neighborPriority = neighbor; -+ } -+ } -+ } -+ } -+ } -+ private void checkPriority() { -+ if (getCurrentPriority() != getDemandedPriority()) this.chunkMap.queueHolderUpdate(this); -+ } -+ -+ public final double getDistance(ServerPlayer player) { -+ return getDistance(player.getX(), player.getZ()); -+ } -+ public final double getDistance(double blockX, double blockZ) { -+ int cx = MCUtil.fastFloor(blockX) >> 4; -+ int cz = MCUtil.fastFloor(blockZ) >> 4; -+ final double x = pos.x - cx; -+ final double z = pos.z - cz; -+ return (x * x) + (z * z); -+ } -+ -+ public final double getDistanceFrom(BlockPos pos) { -+ return getDistance(pos.getX(), pos.getZ()); -+ } -+ -+ @Override -+ public String toString() { -+ return "PlayerChunk{" + -+ "location=" + pos + -+ ", ticketLevel=" + ticketLevel + "/" + getStatus(this.ticketLevel) + -+ ", chunkHolderStatus=" + getChunkHolderStatus() + -+ ", neighborPriority=" + getNeighborsPriority() + -+ ", priority=(" + ticketLevel + " - " + priorityBoost +" vs N " + neighborPriority + ") = " + getDemandedPriority() + " A " + getCurrentPriority() + -+ '}'; -+ } -+ // Paper end - - public ChunkHolder(ChunkPos pos, int level, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { - this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); -@@ -0,0 +0,0 @@ public class ChunkHolder { - } - return null; - } -+ public static ChunkStatus getNextStatus(ChunkStatus status) { -+ if (status == ChunkStatus.FULL) { -+ return status; -+ } -+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1); -+ } -+ public CompletableFuture> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) { -+ return ensureMain(getFutureIfPresentUnchecked(chunkstatus)); -+ } -+ public CompletableFuture ensureMain(CompletableFuture future) { -+ return future.thenApplyAsync(r -> r, chunkMap.mainInvokingExecutor); -+ } - // Paper end - - public CompletableFuture> getFutureIfPresentUnchecked(ChunkStatus leastStatus) { -@@ -0,0 +0,0 @@ public class ChunkHolder { - return this.queueLevel; - } - -+ private void setPriority(int i) { setQueueLevel(i); } // Paper - OBFHELPER - private void setQueueLevel(int level) { - this.queueLevel = level; - } -@@ -0,0 +0,0 @@ public class ChunkHolder { - // CraftBukkit start - // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins. - if (playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { -- this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { -+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main - LevelChunk chunk = (LevelChunk)either.left().orElse(null); - if (chunk != null) { - chunkStorage.callbackExecutor.execute(() -> { -@@ -0,0 +0,0 @@ public class ChunkHolder { - if (!flag2 && flag3) { - // Paper start - cache ticking ready status - int expectCreateCount = ++this.fullChunkCreateCount; -- this.fullChunkFuture = chunkStorage.unpackTicks(this); this.fullChunkFuture.thenAccept((either) -> { -+ this.fullChunkFuture = chunkStorage.unpackTicks(this); ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main - if (either.left().isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) { - // note: Here is a very good place to add callbacks to logic waiting on this. - LevelChunk fullChunk = either.left().get(); - ChunkHolder.this.isFullChunkReady = true; - fullChunk.playerChunk = ChunkHolder.this; -+ this.chunkMap.distanceManager.clearPriorityTickets(pos); - - - } -@@ -0,0 +0,0 @@ public class ChunkHolder { - - if (!flag4 && flag5) { - // Paper start - cache ticking ready status -- this.tickingChunkFuture = chunkStorage.postProcess(this); this.tickingChunkFuture.thenAccept((either) -> { -+ this.tickingChunkFuture = chunkStorage.postProcess(this); ensureMain(this.tickingChunkFuture).thenAccept((either) -> { // Paper - ensure main - if (either.left().isPresent()) { - // note: Here is a very good place to add callbacks to logic waiting on this. - LevelChunk tickingChunk = either.left().get(); -@@ -0,0 +0,0 @@ public class ChunkHolder { - } - - // Paper start - cache ticking ready status -- this.entityTickingChunkFuture = chunkStorage.getEntityTickingRangeFuture(this.pos); this.entityTickingChunkFuture.thenAccept((either) -> { -+ this.entityTickingChunkFuture = chunkStorage.getEntityTickingRangeFuture(this.pos); ensureMain(this.entityTickingChunkFuture).thenAccept((either) -> { // Paper ensureMain - if (either.left().isPresent()) { - // note: Here is a very good place to add callbacks to logic waiting on this. - LevelChunk entityTickingChunk = either.left().get(); -@@ -0,0 +0,0 @@ public class ChunkHolder { - this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; - } - -- this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel); -+ // Paper start - raise IO/load priority if priority changes, use our preferred priority -+ priorityBoost = chunkMap.distanceManager.getChunkPriority(pos); -+ int priority = getDemandedPriority(); -+ if (getCurrentPriority() > priority) { -+ int ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY; -+ if (priority <= 10) { -+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; -+ } else if (priority <= 20) { -+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; -+ } -+ chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority); -+ } -+ if (getCurrentPriority() != priority) { -+ this.onLevelChange.onLevelChange(this.pos, this::getCurrentPriority, priority, this::setPriority); // use preferred priority -+ int neighborsPriority = getNeighborsPriority(); -+ this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority)); -+ } -+ // Paper end - this.oldTicketLevel = this.ticketLevel; - // CraftBukkit start - // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins. - if (!playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) { -- this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { -+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main - LevelChunk chunk = (LevelChunk)either.left().orElse(null); - if (chunk != null) { - chunkStorage.callbackExecutor.execute(() -> { -@@ -0,0 +0,0 @@ public class ChunkHolder { - - public interface LevelChangeListener { - -+ default void changePriority(ChunkPos chunkcoordintpair, IntSupplier intsupplier, int i, IntConsumer intconsumer) { onLevelChange(chunkcoordintpair, intsupplier, i, intconsumer); } // Paper - OBFHELPER - void onLevelChange(ChunkPos pos, IntSupplier levelGetter, int targetLevel, IntConsumer levelSetter); - } - -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 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; - import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - import it.unimi.dsi.fastutil.longs.Long2ByteMap; - import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; -+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; // Paper - import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; - import it.unimi.dsi.fastutil.longs.LongIterator; -@@ -0,0 +0,0 @@ import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; - import net.minecraft.ReportedException; - import net.minecraft.Util; -+import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.protocol.Packet; -@@ -0,0 +0,0 @@ import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; - - import org.bukkit.entity.Player; // CraftBukkit -+import org.spigotmc.AsyncCatcher; - - public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { - -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - public final ServerLevel level; - private final ThreadedLevelLightEngine lightEngine; - private final BlockableEventLoop mainThreadExecutor; -+ final java.util.concurrent.Executor mainInvokingExecutor; // Paper - public final ChunkGenerator generator; - private final Supplier overworldDataStorage; public final Supplier getWorldPersistentDataSupplier() { return this.overworldDataStorage; } // Paper - OBFHELPER - private final PoiManager poiManager; -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - @Override - public void execute(Runnable runnable) { -+ AsyncCatcher.catchOp("Callback Executor execute"); - if (queued == null) { - queued = new java.util.ArrayDeque<>(); - } -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - @Override - public void run() { -+ AsyncCatcher.catchOp("Callback Executor run"); - if (queued == null) { - return; - } -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.level = worldserver; - this.generator = chunkGenerator; - this.mainThreadExecutor = mainThreadExecutor; -+ // Paper start -+ this.mainInvokingExecutor = (run) -> { -+ if (MCUtil.isMainThread()) { -+ run.run(); -+ } else { -+ mainThreadExecutor.execute(run); -+ } -+ }; -+ // Paper end - ProcessorMailbox threadedmailbox = ProcessorMailbox.create(workerExecutor, "worldgen"); - - mainThreadExecutor.getClass(); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, - com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newState) -> { -+ checkHighPriorityChunks(player); - if (newState.size() != 1) { - return; - } -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ); - ChunkMap.this.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update -- }); -+ ChunkMap.this.level.getChunkSource().clearPriorityTickets(chunkPos); -+ }, (player, prevPos, newPos) -> { -+ player.lastHighPriorityChecked = -1; // reset and recheck -+ checkHighPriorityChunks(player); -+ }); - this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets); - this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets, - (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ, -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - }); - // Paper end - no-tick view distance - } -+ // Paper start - Chunk Prioritization -+ public void queueHolderUpdate(ChunkHolder playerchunk) { -+ Runnable runnable = () -> { -+ if (isUnloading(playerchunk)) { -+ return; // unloaded -+ } -+ distanceManager.pendingChunkUpdates.add(playerchunk); -+ if (!distanceManager.pollingPendingChunkUpdates) { -+ level.getChunkSource().runDistanceManagerUpdates(); -+ } -+ }; -+ if (MCUtil.isMainThread()) { -+ // We can't use executor here because it will not execute tasks if its currently in the middle of executing tasks... -+ runnable.run(); -+ } else { -+ mainThreadExecutor.execute(runnable); -+ } -+ } -+ -+ private boolean isUnloading(ChunkHolder playerchunk) { -+ return playerchunk == null || toDrop.contains(playerchunk.pos.toLong()); -+ } -+ -+ private void updateChunkPriorityMap(Long2IntOpenHashMap map, long chunk, int level) { -+ int prev = map.getOrDefault(chunk, -1); -+ if (level > prev) { -+ map.put(chunk, level); -+ } -+ } -+ -+ public void checkHighPriorityChunks(ServerPlayer player) { -+ int currentTick = MinecraftServer.currentTick; -+ if (currentTick - player.lastHighPriorityChecked < 20 || !player.isRealPlayer) { // weed out fake players -+ return; -+ } -+ player.lastHighPriorityChecked = currentTick; -+ Long2IntOpenHashMap priorities = new Long2IntOpenHashMap(); -+ -+ int viewDistance = getEffectiveNoTickViewDistance(); -+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); -+ -+ // Prioritize circular near -+ double playerChunkX = Mth.floor(player.getX()) >> 4; -+ double playerChunkZ = Mth.floor(player.getZ()) >> 4; -+ pos.setValues(player.getX(), 0, player.getZ()); -+ double twoThirdModifier = 2D / 3D; -+ MCUtil.getSpiralOutChunks(pos, Math.min(6, viewDistance)).forEach(coord -> { -+ if (shouldSkipPrioritization(coord)) return; -+ -+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); -+ // Prioritize immediate -+ if (dist <= 4) { -+ updateChunkPriorityMap(priorities, coord.toLong(), (int) (27 - dist)); -+ return; -+ } -+ -+ // Prioritize nearby chunks -+ updateChunkPriorityMap(priorities, coord.toLong(), (int) (20 - dist * twoThirdModifier)); -+ }); -+ -+ // Prioritize Frustum near 3 -+ ChunkPos front3 = player.getChunkInFront(3); -+ pos.setValues(front3.x << 4, 0, front3.z << 4); -+ MCUtil.getSpiralOutChunks(pos, Math.min(5, viewDistance)).forEach(coord -> { -+ if (shouldSkipPrioritization(coord)) return; -+ -+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); -+ updateChunkPriorityMap(priorities, coord.toLong(), (int) (25 - dist * twoThirdModifier)); -+ }); -+ -+ // Prioritize Frustum near 5 -+ if (viewDistance > 4) { -+ ChunkPos front5 = player.getChunkInFront(5); -+ pos.setValues(front5.x << 4, 0, front5.z << 4); -+ MCUtil.getSpiralOutChunks(pos, 4).forEach(coord -> { -+ if (shouldSkipPrioritization(coord)) return; -+ -+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); -+ updateChunkPriorityMap(priorities, coord.toLong(), (int) (25 - dist * twoThirdModifier)); -+ }); -+ } -+ -+ // Prioritize Frustum far 7 -+ if (viewDistance > 6) { -+ ChunkPos front7 = player.getChunkInFront(7); -+ pos.setValues(front7.x << 4, 0, front7.z << 4); -+ MCUtil.getSpiralOutChunks(pos, 3).forEach(coord -> { -+ if (shouldSkipPrioritization(coord)) { -+ return; -+ } -+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); -+ updateChunkPriorityMap(priorities, coord.toLong(), (int) (25 - dist * twoThirdModifier)); -+ }); -+ } -+ -+ if (priorities.isEmpty()) return; -+ distanceManager.delayDistanceManagerTick = true; -+ priorities.long2IntEntrySet().fastForEach(entry -> distanceManager.markHighPriority(new ChunkPos(entry.getLongKey()), entry.getIntValue())); -+ distanceManager.delayDistanceManagerTick = false; -+ level.getChunkSource().runDistanceManagerUpdates(); -+ -+ } -+ -+ private boolean shouldSkipPrioritization(ChunkPos coord) { -+ if (playerViewDistanceNoTickMap.getObjectsInRange(coord.toLong()) == null) return true; -+ ChunkHolder chunk = getUpdatingChunkIfPresent(coord.toLong()); -+ return chunk != null && (chunk.isFullChunkReady()); -+ } -+ // Paper end - - public void updatePlayerMobTypeMap(Entity entity) { - if (!this.level.paperConfig.perPlayerMobSpawns) { -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - List>> list = Lists.newArrayList(); - int j = centerChunk.x; - int k = centerChunk.z; -+ ChunkHolder requestingNeighbor = getUpdatingChunkIfPresent(centerChunk.toLong()); // Paper - - for (int l = -margin; l <= margin; ++l) { - for (int i1 = -margin; i1 <= margin; ++i1) { -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - ChunkStatus chunkstatus = (ChunkStatus) distanceToStatus.apply(j1); - CompletableFuture> completablefuture = playerchunk.getOrScheduleFuture(chunkstatus, this); -+ // Paper start -+ if (requestingNeighbor != null && requestingNeighbor != playerchunk && !completablefuture.isDone()) { -+ requestingNeighbor.onNeighborRequest(playerchunk, chunkstatus); -+ completablefuture.thenAccept(either -> { -+ requestingNeighbor.onNeighborDone(playerchunk, chunkstatus, either.left().orElse(null)); -+ }); -+ } -+ // Paper end - - list.add(completablefuture); - } -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - }; - - CompletableFuture chunkSaveFuture = this.level.asyncChunkTaskManager.getChunkSaveFuture(pos.x, pos.z); -+ ChunkHolder playerChunk = getUpdatingChunkIfPresent(pos.toLong()); -+ int chunkPriority = playerChunk != null ? playerChunk.getCurrentPriority() : 33; -+ int priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY; -+ -+ if (chunkPriority <= 10) { -+ priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; -+ } else if (chunkPriority <= 20) { -+ priority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; -+ } -+ boolean isHighestPriority = priority == com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY; - if (chunkSaveFuture != null) { -- this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, -- com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY, chunkHolderConsumer, false, chunkSaveFuture); -- this.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY); -+ this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, priority, chunkHolderConsumer, isHighestPriority, chunkSaveFuture); - } else { -- this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, -- com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false); -+ this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, priority, chunkHolderConsumer, isHighestPriority); - } -+ this.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, priority); - return ret; - // Paper end - } -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - long i = playerchunk.getPos().toLong(); - - playerchunk.getClass(); -- mailbox.tell(ChunkTaskPriorityQueueSorter.message(runnable, i, playerchunk::getTicketLevel)); -+ mailbox.tell(ChunkTaskPriorityQueueSorter.message(runnable, i, () -> 1)); // Paper - final loads are always urgent! - }); - } - -diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/DistanceManager.java -+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java -@@ -0,0 +0,0 @@ import java.util.Set; - import java.util.concurrent.CompletableFuture; - import java.util.concurrent.Executor; - import javax.annotation.Nullable; -+import net.minecraft.core.BlockPos; - import net.minecraft.core.SectionPos; -+import net.minecraft.server.MCUtil; -+import net.minecraft.server.MinecraftServer; - import net.minecraft.util.SortedArraySet; - import net.minecraft.util.thread.ProcessorHandle; - import net.minecraft.world.level.ChunkPos; -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.ChunkStatus; - import net.minecraft.world.level.chunk.LevelChunk; - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; -+import org.spigotmc.AsyncCatcher; // Paper - - public abstract class DistanceManager { - -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - private final ChunkTaskPriorityQueueSorter ticketThrottler; - private final ProcessorHandle> ticketThrottlerInput; - private final ProcessorHandle ticketThrottlerReleaser; -- private final LongSet ticketsToRelease = new LongOpenHashSet(); -+ private final LongSet ticketsToRelease = new LongOpenHashSet(); public final LongSet getOnPlayerTicketAddQueue() { return ticketsToRelease; } // Paper - OBFHELPER - private final Executor mainThreadExecutor; - private long ticketTickCounter; - -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - } - - private static int getTicketLevelAt(SortedArraySet> arraysetsorted) { -+ AsyncCatcher.catchOp("ChunkMapDistance::getLowestTicketLevel"); // Paper - return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.first()).getTicketLevel() : ChunkMap.MAX_CHUNK_DISTANCE + 1; - } - -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - public boolean runAllUpdates(ChunkMap chunkStorage) { - //this.f.a(); // Paper - no longer used -+ AsyncCatcher.catchOp("DistanceManagerTick"); // Paper - this.playerTicketManager.runAllUpdates(); - int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE); - boolean flag = i != 0; -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - // Paper start - if (!this.pendingChunkUpdates.isEmpty()) { -+ this.pollingPendingChunkUpdates = true; try { - while(!this.pendingChunkUpdates.isEmpty()) { - ChunkHolder remove = this.pendingChunkUpdates.remove(); - remove.isUpdateQueued = false; - remove.updateFutures(chunkStorage); - } -+ } finally { this.pollingPendingChunkUpdates = false; } - // Paper end - return true; - } else { -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - return flag; - } - } -+ boolean pollingPendingChunkUpdates = false; // Paper - - private boolean addTicket(long i, Ticket ticket) { // CraftBukkit - void -> boolean -+ AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper - SortedArraySet> arraysetsorted = this.getTickets(i); - int j = getTicketLevelAt(arraysetsorted); - Ticket ticket1 = (Ticket) arraysetsorted.addOrGet(ticket); // CraftBukkit - decompile error -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - } - - private boolean removeTicket(long i, Ticket ticket) { // CraftBukkit - void -> boolean -+ AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper - SortedArraySet> arraysetsorted = this.getTickets(i); -+ int oldLevel = getTicketLevelAt(arraysetsorted); // Paper - - boolean removed = false; // CraftBukkit - if (arraysetsorted.remove(ticket)) { -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - this.tickets.remove(i); - } - -- this.ticketTracker.update(i, getTicketLevelAt(arraysetsorted), false); -+ int newLevel = getTicketLevelAt(arraysetsorted); // Paper -+ if (newLevel > oldLevel) this.ticketTracker.update(i, newLevel, false); // Paper - return removed; // CraftBukkit - } - -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - this.addTicketAtLevel(type, pos, level, argument); - } - -+ // Paper start -+ public static final int PRIORITY_TICKET_LEVEL = ChunkMap.MAX_CHUNK_DISTANCE; -+ public static final int URGENT_PRIORITY = 29; -+ public boolean delayDistanceManagerTick = false; -+ public boolean markUrgent(ChunkPos coords) { -+ return addPriorityTicket(coords, TicketType.URGENT, URGENT_PRIORITY); -+ } -+ public boolean markHighPriority(ChunkPos coords, int priority) { -+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority)); -+ return addPriorityTicket(coords, TicketType.PRIORITY, priority); -+ } -+ -+ public void markAreaHighPriority(ChunkPos center, int priority, int radius) { -+ delayDistanceManagerTick = true; -+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority)); -+ int finalPriority = priority; -+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> { -+ addPriorityTicket(coords, TicketType.PRIORITY, finalPriority); -+ }); -+ delayDistanceManagerTick = false; -+ chunkMap.level.getChunkSource().runDistanceManagerUpdates(); -+ } -+ -+ public void clearAreaPriorityTickets(ChunkPos center, int radius) { -+ delayDistanceManagerTick = true; -+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> { -+ this.removeTicket(coords.toLong(), new Ticket(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords)); -+ }); -+ delayDistanceManagerTick = false; -+ chunkMap.level.getChunkSource().runDistanceManagerUpdates(); -+ } -+ -+ private boolean hasPlayerTicket(ChunkPos coords, int level) { -+ SortedArraySet> tickets = this.tickets.get(coords.toLong()); -+ if (tickets == null || tickets.isEmpty()) { -+ return false; -+ } -+ for (Ticket ticket : tickets) { -+ if (ticket.getType() == TicketType.PLAYER && ticket.getTicketLevel() == level) { -+ return true; -+ } -+ } -+ -+ return false; -+ } -+ -+ private boolean addPriorityTicket(ChunkPos coords, TicketType ticketType, int priority) { -+ AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket"); -+ long pair = coords.toLong(); -+ ChunkHolder chunk = chunkMap.getUpdatingChunkIfPresent(pair); -+ boolean needsTicket = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null && !hasPlayerTicket(coords, 33); -+ -+ if (needsTicket) { -+ Ticket ticket = new Ticket<>(TicketType.PLAYER, 33, coords); -+ getOnPlayerTicketAddQueue().add(pair); -+ addTicket(pair, ticket); -+ } -+ if ((chunk != null && chunk.isFullChunkReady())) { -+ if (needsTicket) { -+ chunkMap.level.getChunkSource().runDistanceManagerUpdates(); -+ } -+ return needsTicket; -+ } -+ -+ boolean success; -+ if (!(success = updatePriorityTicket(coords, ticketType, priority))) { -+ Ticket ticket = new Ticket(ticketType, PRIORITY_TICKET_LEVEL, coords); -+ ticket.priority = priority; -+ success = this.addTicket(pair, ticket); -+ } else { -+ if (chunk == null) { -+ chunk = chunkMap.getUpdatingChunkIfPresent(pair); -+ } -+ chunkMap.queueHolderUpdate(chunk); -+ } -+ -+ //chunkMap.world.getWorld().spawnParticle(priority <= 15 ? org.bukkit.Particle.EXPLOSION_HUGE : org.bukkit.Particle.EXPLOSION_NORMAL, chunkMap.world.getWorld().getPlayers(), null, coords.x << 4, 70, coords.z << 4, 2, 0, 0, 0, 1, null, true); -+ -+ chunkMap.level.getChunkSource().runDistanceManagerUpdates(); -+ -+ return success; -+ } -+ -+ private boolean updatePriorityTicket(ChunkPos coords, TicketType type, int priority) { -+ SortedArraySet> tickets = this.tickets.get(coords.toLong()); -+ if (tickets == null) { -+ return false; -+ } -+ for (Ticket ticket : tickets) { -+ if (ticket.getType() == type) { -+ // We only support increasing, not decreasing, too complicated -+ ticket.setCurrentTick(this.ticketTickCounter); -+ ticket.priority = Math.max(ticket.priority, priority); -+ return true; -+ } -+ } -+ -+ return false; -+ } -+ -+ public int getChunkPriority(ChunkPos coords) { -+ AsyncCatcher.catchOp("ChunkMapDistance::getChunkPriority"); -+ SortedArraySet> tickets = this.tickets.get(coords.toLong()); -+ if (tickets == null) { -+ return 0; -+ } -+ for (Ticket ticket : tickets) { -+ if (ticket.getType() == TicketType.URGENT) { -+ return URGENT_PRIORITY; -+ } -+ } -+ for (Ticket ticket : tickets) { -+ if (ticket.getType() == TicketType.PRIORITY && ticket.priority > 0) { -+ return ticket.priority; -+ } -+ } -+ return 0; -+ } -+ -+ public void clearPriorityTickets(ChunkPos coords) { -+ AsyncCatcher.catchOp("ChunkMapDistance::clearPriority"); -+ this.removeTicket(coords.toLong(), new Ticket(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords)); -+ } -+ -+ public void clearUrgent(ChunkPos coords) { -+ AsyncCatcher.catchOp("ChunkMapDistance::clearUrgent"); -+ this.removeTicket(coords.toLong(), new Ticket(TicketType.URGENT, PRIORITY_TICKET_LEVEL, coords)); -+ } -+ // Paper end - public boolean addTicketAtLevel(TicketType ticketType, ChunkPos chunkcoordintpair, int level, T identifier) { - return this.addTicket(chunkcoordintpair.toLong(), new Ticket<>(ticketType, level, identifier)); - // CraftBukkit end -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker { - -- private int viewDistance = 0; -+ private int viewDistance = 0; private int getViewDistance() { return viewDistance; } private void setViewDistance(int value) { this.viewDistance = value; } // Paper - OBFHELPER - private final Long2IntMap queueLevels = Long2IntMaps.synchronize(new Long2IntOpenHashMap()); - private final LongSet toUpdate = new LongOpenHashSet(); - -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - public void updateViewDistance(int watchDistance) { - ObjectIterator objectiterator = this.chunks.long2ByteEntrySet().iterator(); -+ // Paper start - set the view distance before scheduling chunk loads/unloads -+ int lastViewDistance = getViewDistance(); -+ setViewDistance(watchDistance); -+ // Paper end - - while (objectiterator.hasNext()) { - Long2ByteMap.Entry it_unimi_dsi_fastutil_longs_long2bytemap_entry = (Long2ByteMap.Entry) objectiterator.next(); // Paper - decompile fix - byte b0 = it_unimi_dsi_fastutil_longs_long2bytemap_entry.getByteValue(); - long j = it_unimi_dsi_fastutil_longs_long2bytemap_entry.getLongKey(); - -- this.onLevelChange(j, b0, this.haveTicketFor(b0), b0 <= watchDistance - 2); -+ this.onLevelChange(j, b0, b0 <= lastViewDistance - 2, this.haveTicketFor(b0)); // Paper - } - -- this.viewDistance = watchDistance; -+ //this.e = i; // Paper - view distance is now set further up - } - - private void onLevelChange(long pos, int distance, boolean oldWithinViewDistance, boolean withinViewDistance) { - if (oldWithinViewDistance != withinViewDistance) { -- Ticket ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkPos(pos)); // Paper - no-tick view distance -+ ChunkPos coords = new ChunkPos(pos); // Paper -+ Ticket ticket = new Ticket<>(TicketType.PLAYER, 33, coords); // Paper - no-tick view distance - - if (withinViewDistance) { -- DistanceManager.this.ticketThrottlerInput.tell(ChunkTaskPriorityQueueSorter.message(() -> { -+ scheduleChunkLoad(pos, MinecraftServer.currentTick, distance, (priority) -> { // Paper - smarter ticket delay based on frustum and distance -+ // Paper start - recheck its still valid if not cancel -+ if (!isChunkInRange(pos)) { -+ DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { -+ DistanceManager.this.mainThreadExecutor.execute(() -> { -+ DistanceManager.this.removeTicket(pos, ticket); -+ DistanceManager.this.clearPriorityTickets(coords); -+ }); -+ }, pos, false)); -+ return; -+ } -+ // abort early if we got a ticket already -+ if (hasPlayerTicket(coords, 33)) return; -+ // skip player ticket throttle for near chunks -+ if (priority <= 3) { -+ DistanceManager.this.addTicket(pos, ticket); -+ DistanceManager.this.ticketsToRelease.add(pos); -+ return; -+ } -+ // Paper end -+ DistanceManager.this.ticketThrottlerInput.tell(ChunkTaskPriorityQueueSorter.message(() -> { // CraftBukkit - decompile error - DistanceManager.this.mainThreadExecutor.execute(() -> { -- if (this.haveTicketFor(this.getLevel(pos))) { -+ if (isChunkInRange(pos)) { if (!hasPlayerTicket(coords, 33)) { // Paper - high priority might of already added it - DistanceManager.this.addTicket(pos, ticket); - DistanceManager.this.ticketsToRelease.add(pos); -- } else { -- DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { -+ }} else { // Paper -+ DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { // CraftBukkit - decompile error - }, pos, false)); - } - - }); - }, pos, () -> { -- return distance; -+ return Math.min(ChunkMap.MAX_CHUNK_DISTANCE, priority); // Paper - })); -+ }); // Paper - } else { - DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { - DistanceManager.this.mainThreadExecutor.execute(() -> { - DistanceManager.this.removeTicket(pos, ticket); -+ DistanceManager.this.clearPriorityTickets(coords); // Paper - }); - }, pos, true)); - } -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - } - -+ // Paper start - smart scheduling of player tickets -+ private boolean isChunkInRange(long i) { -+ return this.isLoadedChunkLevel(this.getChunkLevel(i)); -+ } -+ public void scheduleChunkLoad(long i, long startTick, int initialDistance, java.util.function.Consumer task) { -+ long elapsed = MinecraftServer.currentTick - startTick; -+ ChunkPos chunkPos = new ChunkPos(i); -+ ChunkHolder updatingChunk = chunkMap.getUpdatingChunkIfPresent(i); -+ if ((updatingChunk != null && updatingChunk.isFullChunkReady()) || !isChunkInRange(i) || getChunkPriority(chunkPos) > 0) { // Copied from above -+ // no longer needed -+ task.accept(1); -+ return; -+ } -+ -+ int desireDelay = 0; -+ double minDist = Double.MAX_VALUE; -+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet players = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i); -+ if (elapsed == 0 && initialDistance <= 4) { -+ // Aim for no delay on initial 6 chunk radius tickets save on performance of the below code to only > 6 -+ minDist = initialDistance; -+ } else if (players != null) { -+ Object[] backingSet = players.getBackingSet(); -+ -+ BlockPos blockPos = chunkPos.asPosition(); -+ -+ boolean isFront = false; -+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); -+ for (int index = 0, len = backingSet.length; index < len; ++index) { -+ if (!(backingSet[index] instanceof ServerPlayer)) { -+ continue; -+ } -+ ServerPlayer player = (ServerPlayer) backingSet[index]; -+ -+ ChunkPos pointInFront = player.getChunkInFront(5); -+ pos.setValues(pointInFront.x << 4, 0, pointInFront.z << 4); -+ double frontDist = MCUtil.distanceSq(pos, blockPos); -+ -+ pos.setValues(player.getX(), 0, player.getZ()); -+ double center = MCUtil.distanceSq(pos, blockPos); -+ -+ double dist = Math.min(frontDist, center); -+ if (!isFront) { -+ ChunkPos pointInBack = player.getChunkInFront(-7); -+ pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4); -+ double backDist = MCUtil.distanceSq(pos, blockPos); -+ if (frontDist < backDist) { -+ isFront = true; -+ } -+ } -+ if (dist < minDist) { -+ minDist = dist; -+ } -+ } -+ if (minDist == Double.MAX_VALUE) { -+ minDist = 15; -+ } else { -+ minDist = Math.sqrt(minDist) / 16; -+ } -+ if (minDist > 4) { -+ int desiredTimeDelayMax = isFront ? -+ (minDist < 10 ? 7 : 15) : // Front -+ (minDist < 10 ? 15 : 45); // Back -+ desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32); -+ } -+ } else { -+ minDist = initialDistance; -+ desireDelay = 1; -+ } -+ long delay = desireDelay - elapsed; -+ if (delay <= 0 && minDist > 4 && minDist < Double.MAX_VALUE) { -+ boolean hasAnyNeighbor = false; -+ for (int x = -1; x <= 1; x++) { -+ for (int z = -1; z <= 1; z++) { -+ if (x == 0 && z == 0) continue; -+ long pair = ChunkPos.asLong(chunkPos.x + x, chunkPos.z + z); -+ ChunkHolder neighbor = chunkMap.getUpdatingChunkIfPresent(pair); -+ ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null; -+ if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) { -+ hasAnyNeighbor = true; -+ } -+ } -+ } -+ if (!hasAnyNeighbor) { -+ delay += 20; -+ } -+ } -+ if (delay <= 0) { -+ task.accept((int) minDist); -+ } else { -+ int taskDelay = (int) Math.min(delay, minDist >= 10 ? 40 : (minDist < 6 ? 5 : 20)); -+ MCUtil.scheduleTask(taskDelay, () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer"); -+ } -+ } -+ // Paper end -+ - @Override - public void runAllUpdates() { - super.runAllUpdates(); -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - - } - -+ private boolean isLoadedChunkLevel(int i) { return haveTicketFor(i); } // Paper - OBFHELPER - private boolean haveTicketFor(int distance) { - return distance <= this.viewDistance - 2; - } -@@ -0,0 +0,0 @@ public abstract class DistanceManager { - this.chunks.defaultReturnValue((byte) (i + 2)); - } - -+ protected final int getChunkLevel(long i) { return getLevel(i); } // Paper - OBFHELPER - @Override - protected int getLevel(long id) { - return this.chunks.get(id); -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 { - public void removeTicketAtLevel(TicketType ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { - this.distanceManager.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); - } -+ -+ public boolean markUrgent(ChunkPos coords) { -+ return this.distanceManager.markUrgent(coords); -+ } -+ -+ public boolean markHighPriority(ChunkPos coords, int priority) { -+ return this.distanceManager.markHighPriority(coords, priority); -+ } -+ -+ public void markAreaHighPriority(ChunkPos center, int priority, int radius) { -+ this.distanceManager.markAreaHighPriority(center, priority, radius); -+ } -+ -+ public void clearAreaPriorityTickets(ChunkPos center, int radius) { -+ this.distanceManager.clearAreaPriorityTickets(center, radius); -+ } -+ -+ public void clearPriorityTickets(ChunkPos coords) { -+ this.distanceManager.clearPriorityTickets(coords); -+ } - // Paper end - - @Nullable -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - - if (!completablefuture.isDone()) { // Paper - // Paper start - async chunk io/loading -+ ChunkPos pair = new ChunkPos(x1, z1); -+ this.distanceManager.markUrgent(pair); - this.level.asyncChunkTaskManager.raisePriority(x1, z1, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); - com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.level, x1, z1); - // Paper end -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - this.mainThreadProcessor.managedBlock(completablefuture::isDone); - com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug - this.level.timings.syncChunkLoad.stopTiming(); // Paper -+ this.distanceManager.clearPriorityTickets(pair); // Paper -+ this.distanceManager.clearUrgent(pair); // Paper - } // Paper - ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { - return ichunkaccess1; -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - if (flag && !currentlyUnloading) { - // CraftBukkit end - this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair); -+ if (isUrgent) this.distanceManager.markUrgent(chunkcoordintpair); // Paper - if (this.chunkAbsent(playerchunk, l)) { - ProfilerFiller gameprofilerfiller = this.level.getProfiler(); - - gameprofilerfiller.push("chunkLoad"); -+ distanceManager.delayDistanceManagerTick = false; // Paper - ensure this is never false - this.runDistanceManagerUpdates(); - playerchunk = this.getVisibleChunkIfPresent(k); - gameprofilerfiller.pop(); -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - } - } - } -- -- return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(chunkstatus, this.chunkMap); -+ // Paper start -+ CompletableFuture> future = this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(chunkstatus, this.chunkMap); -+ if (isUrgent) { -+ future.thenAccept(either -> this.distanceManager.clearUrgent(chunkcoordintpair)); -+ } -+ return future; -+ // Paper end - } - - private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) { -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - } - - public boolean runDistanceManagerUpdates() { // Paper - private -> public -+ if (distanceManager.delayDistanceManagerTick) return false; // Paper - boolean flag = this.distanceManager.runAllUpdates(this.chunkMap); - boolean flag1 = this.chunkMap.promoteChunkMap(); - -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 net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket; - import net.minecraft.network.protocol.game.ServerboundClientInformationPacket; - import net.minecraft.resources.ResourceKey; - import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MCUtil; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.PlayerAdvancements; - import net.minecraft.server.network.ServerGamePacketListenerImpl; -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { - private int lastRecordedArmor = Integer.MIN_VALUE; - private int lastRecordedLevel = Integer.MIN_VALUE; - private int lastRecordedExperience = Integer.MIN_VALUE; -+ public long lastHighPriorityChecked; // Paper -+ public void forceCheckHighPriority() { -+ lastHighPriorityChecked = -1; -+ getLevel().getChunkSource().chunkMap.checkHighPriorityChunks(this); -+ } -+ public boolean isRealPlayer; // Paper - private float lastSentHealth = -1.0E8F; - private int lastSentFood = -99999999; - private boolean lastFoodSaturationZero = true; -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { - this.maxHealthCache = this.getMaxHealth(); - this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper - } -+ // Paper start -+ public BlockPos getPointInFront(double inFront) { -+ double rads = Math.toRadians(MCUtil.normalizeYaw(this.yRot+90)); // MC rotates yaw 90 for some odd reason -+ final double x = getX() + inFront * Math.cos(rads); -+ final double z = getZ() + inFront * Math.sin(rads); -+ return new BlockPos(x, getY(), z); -+ } -+ -+ public ChunkPos getChunkInFront(double inFront) { -+ double rads = Math.toRadians(MCUtil.normalizeYaw(this.yRot+90)); // MC rotates yaw 90 for some odd reason -+ final double x = getX() + (inFront * 16) * Math.cos(rads); -+ final double z = getZ() + (inFront * 16) * Math.sin(rads); -+ return new ChunkPos(Mth.floor(x) >> 4, Mth.floor(z) >> 4); -+ } -+ // Paper end - - // Yes, this doesn't match Vanilla, but it's the best we can do for now. - // If this is an issue, PRs are welcome -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player implements ContainerListener { - if (valid && !this.isSpectator() || this.level.hasChunkAt(this.blockPosition())) { // Paper - don't tick dead players that are not in the world currently (pending respawn) - super.tick(); - } -+ if (valid && isAlive() && connection != null) ((ServerLevel)level).getChunkSource().chunkMap.checkHighPriorityChunks(this); // Paper - - for (int i = 0; i < this.inventory.getContainerSize(); ++i) { - ItemStack itemstack = this.inventory.getItem(i); -diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/Ticket.java -+++ b/src/main/java/net/minecraft/server/level/Ticket.java -@@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { - private final int ticketLevel; - public final T key; public final T getObjectReason() { return this.key; } // Paper - OBFHELPER - private long createdTick; public final long getCreationTick() { return this.createdTick; } // Paper - OBFHELPER -+ public int priority = 0; // Paper - - protected Ticket(TicketType type, int level, T argument) { - this.type = type; -@@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { - return this.ticketLevel; - } - -+ public final void setCurrentTick(long i) { this.setCreatedTick(i); } // Paper - OBFHELPER - protected void setCreatedTick(long tickCreated) { - this.createdTick = tickCreated; - } -diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/TicketType.java -+++ b/src/main/java/net/minecraft/server/level/TicketType.java -@@ -0,0 +0,0 @@ public class TicketType { - public static final TicketType PLUGIN_TICKET = create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit - public static final TicketType FUTURE_AWAIT = create("future_await", Long::compareTo); // Paper - public static final TicketType ASYNC_LOAD = create("async_load", Long::compareTo); // Paper -+ public static final TicketType PRIORITY = create("priority", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper -+ public static final TicketType URGENT = create("urgent", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper - - public static TicketType create(String name, Comparator comparator) { - return new TicketType<>(name, comparator, 0L); -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { - - this.awaitingTeleportTime = this.tickCount; - this.player.absMoveTo(d0, d1, d2, f, f1); -+ this.player.forceCheckHighPriority(); // Paper - this.player.connection.send(new ClientboundPlayerPositionPacket(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport)); - } - -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 { - final ChunkPos pos = new ChunkPos(chunkX, chunkZ); - ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap; - playerChunkMap.getChunkDistanceManager().addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.toLong()); -- worldserver1.getChunkSource().runDistanceManagerUpdates(); -- worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> { -+ worldserver1.getChunkSource().markAreaHighPriority(pos, 28, 3); -+ worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, false).thenApply(chunk -> { - ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong()); - if (updatingChunk != null) { - return updatingChunk.getEntityTickingFuture(); -@@ -0,0 +0,0 @@ public abstract class PlayerList { - SocketAddress socketaddress = loginlistener.connection.getRemoteAddress(); - - ServerPlayer entity = new ServerPlayer(this.server, this.server.getLevel(Level.OVERWORLD), gameprofile, new ServerPlayerGameMode(this.server.getLevel(Level.OVERWORLD))); -+ entity.isRealPlayer = true; // Paper - Player player = entity.getBukkitEntity(); - PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.getRawAddress()).getAddress()); - -@@ -0,0 +0,0 @@ public abstract class PlayerList { - // CraftBukkit end - - worldserver1.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, new ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper -+ entityplayer1.forceCheckHighPriority(); // Player - while (avoidSuffocation && !worldserver1.noCollision(entityplayer1) && entityplayer1.getY() < 256.0D) { - entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); - } -diff --git a/src/main/java/net/minecraft/world/level/ChunkPos.java b/src/main/java/net/minecraft/world/level/ChunkPos.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/ChunkPos.java -+++ b/src/main/java/net/minecraft/world/level/ChunkPos.java -@@ -0,0 +0,0 @@ public class ChunkPos { - return "[" + this.x + ", " + this.z + "]"; - } - -+ public final BlockPos asPosition() { return getWorldPosition(); } // Paper - OBFHELPER - public BlockPos getWorldPosition() { - return new BlockPos(this.getMinBlockX(), 0, this.getMinBlockZ()); - } -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - return future; - } - -+ if (!urgent) { -+ // if not urgent, at least use a slightly boosted priority -+ world.getChunkSource().markHighPriority(new ChunkPos(x, z), 1); -+ } - return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { - net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null); - return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -0,0 +0,0 @@ import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; - import net.minecraft.server.network.ServerGamePacketListenerImpl; - import net.minecraft.server.players.UserWhiteListEntry; -+import net.minecraft.util.Mth; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.ExperienceOrb; - import net.minecraft.world.entity.LivingEntity; -@@ -0,0 +0,0 @@ import net.minecraft.world.entity.ai.attributes.Attributes; - import net.minecraft.world.inventory.AbstractContainerMenu; - import net.minecraft.world.item.enchantment.EnchantmentHelper; - import net.minecraft.world.item.enchantment.Enchantments; -+import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.GameType; - import net.minecraft.world.level.biome.BiomeManager; - import net.minecraft.world.level.block.entity.SignBlockEntity; -@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead."); - } - -+ // Paper start -+ @Override -+ public java.util.concurrent.CompletableFuture teleportAsync(Location loc, @javax.annotation.Nonnull PlayerTeleportEvent.TeleportCause cause) { -+ ((CraftWorld)loc.getWorld()).getHandle().getChunkSource().markAreaHighPriority(new ChunkPos(Mth.floor(loc.getX()) >> 4, Mth.floor(loc.getZ()) >> 4), 28, 3); // Paper - load area high priority -+ return super.teleportAsync(loc, cause); -+ } -+ // Paper end -+ - @Override - public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { - Preconditions.checkArgument(location != null, "location"); diff --git a/patches/server-remapped/Improve-Chunk-Status-Transition-Speed.patch b/patches/server-remapped/Improve-Chunk-Status-Transition-Speed.patch deleted file mode 100644 index b60b13d7d7..0000000000 --- a/patches/server-remapped/Improve-Chunk-Status-Transition-Speed.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Fri, 29 May 2020 23:32:14 -0400 -Subject: [PATCH] Improve Chunk Status Transition Speed - -When a chunk is loaded from disk that has already been generated, -the server has to promote the chunk through the system to reach -it's current desired status level. - -This results in every single status transition going from the main thread -to the world gen threads, only to discover it has no work it actually -needs to do.... and then it returns back to main. - -This back and forth costs a lot of time and can really delay chunk loads -when the server is under high TPS due to their being a lot of time in -between chunk load times, as well as hogs up the chunk threads from doing -actual generation and light work. - -Additionally, the whole task system uses a lot of CPU on the server threads anyways. - -So by optimizing status transitions for status's that are already complete, -we can run them to the desired level while on main thread (where it has -to happen anyways) instead of ever jumping to world gen thread. - -This will improve chunk loading effeciency to be reduced down to the following -scenario / path: - -1) MAIN: Chunk Requested, Load Request sent to ChunkTaskManager / IO Queue -2) IO: Once position in queue comes, submit read IO data and schedule to chunk task thread -3) CHUNK: Once IO is loaded and position in queue comes, deserialize the chunk data, process conversions, submit to main queue -4) MAIN: next Chunk Task process (Mid Tick or End Of Tick), load chunk data into world (POI, main thread tasks) -5) MAIN: process status transitions all the way to LIGHT, light schedules Threaded task -6) SERVER: Light tasks register light enablement for chunk and any lighting needing to be done -7) MAIN: Task returns to main, finish processing to FULL/TICKING status - -Previously would have hopped to SERVER around 12+ times there extra. - -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -0,0 +0,0 @@ public class ChunkHolder { - this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); - } - // Paper end - optimise isOutsideOfRange -+ // Paper start - optimize chunk status progression without jumping through thread pool -+ public boolean canAdvanceStatus() { -+ ChunkStatus status = getChunkHolderStatus(); -+ ChunkAccess chunk = getAvailableChunkNow(); -+ return chunk != null && (status == null || chunk.getStatus().isAtLeastStatus(getNextStatus(status))); -+ } -+ // Paper end - - // Paper start - no-tick view distance - public final LevelChunk getSendingChunk() { -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 - return either.mapLeft((list) -> { - return (LevelChunk) list.get(list.size() / 2); - }); -- }, this.mainThreadExecutor); -+ }, this.mainInvokingExecutor); // Paper - } - - @Nullable -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ChunkAccess ichunkaccess = (ChunkAccess) optional.get(); - - if (ichunkaccess.getStatus().isOrAfter(requiredStatus)) { -- CompletableFuture completablefuture1; -+ CompletableFuture> completablefuture1; // Paper - - if (requiredStatus == ChunkStatus.LIGHT) { - completablefuture1 = this.scheduleChunkGeneration(holder, requiredStatus); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return this.scheduleChunkGeneration(holder, requiredStatus); - } - } -- }, this.mainThreadExecutor); -+ }, this.mainInvokingExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainInvokingExecutor); // Paper - optimize chunk status progression without jumping through thread pool - ensure main - } - } - -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return CompletableFuture.completedFuture(Either.right(playerchunk_failure)); - }); - }, (runnable) -> { -+ // Paper start - optimize chunk status progression without jumping through thread pool -+ if (holder.canAdvanceStatus()) { -+ this.mainInvokingExecutor.execute(runnable); -+ return; -+ } -+ // Paper end - this.worldgenMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable)); - }); - } diff --git a/patches/server-remapped/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/patches/server-remapped/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch deleted file mode 100644 index bf65a9c0dc..0000000000 --- a/patches/server-remapped/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sun, 7 Jun 2020 19:25:13 -0400 -Subject: [PATCH] Use seed based lookup for Treasure Maps - Fixes lag from - carto/sunken maps - - -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 net.minecraft.world.level.Level implements Worl - this.worldDataServer.setThundering(thundering); - } - -- @Override -- public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { -+ public Biome getBiomeBySeed(int i, int j, int k) { return getUncachedNoiseBiome(i, j, k); } // Paper - OBFHELPER -+ @Override public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { - return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ); - } - -diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/item/MapItem.java -+++ b/src/main/java/net/minecraft/world/item/MapItem.java -@@ -0,0 +0,0 @@ public class MapItem extends ComplexItem { - - for (l = 0; l < 128 * i; ++l) { - for (i1 = 0; i1 < 128 * i; ++i1) { -- abiomebase[l * 128 * i + i1] = worldserver.getBiome(new BlockPos((j / i - 64) * i + i1, 0, (k / i - 64) * i + l)); -+ abiomebase[l * 128 * i + i1] = worldserver.getBiomeBySeed((j / i - 64) * i + i1, 0, (k / i - 64) * i + l); // Paper - } - } - diff --git a/patches/server-remapped/Fix-villager-trading-demand-MC-163962.patch b/patches/server/Fix-villager-trading-demand-MC-163962.patch similarity index 100% rename from patches/server-remapped/Fix-villager-trading-demand-MC-163962.patch rename to patches/server/Fix-villager-trading-demand-MC-163962.patch diff --git a/patches/server-remapped/Maps-shouldn-t-load-chunks.patch b/patches/server/Maps-shouldn-t-load-chunks.patch similarity index 100% rename from patches/server-remapped/Maps-shouldn-t-load-chunks.patch rename to patches/server/Maps-shouldn-t-load-chunks.patch diff --git a/patches/server-remapped/Optimize-Bit-Operations-by-inlining.patch b/patches/server/Optimize-Bit-Operations-by-inlining.patch similarity index 74% rename from patches/server-remapped/Optimize-Bit-Operations-by-inlining.patch rename to patches/server/Optimize-Bit-Operations-by-inlining.patch index 02af9b112c..1e6da24bb7 100644 --- a/patches/server-remapped/Optimize-Bit-Operations-by-inlining.patch +++ b/patches/server/Optimize-Bit-Operations-by-inlining.patch @@ -15,17 +15,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private static final Logger LOGGER = LogManager.getLogger(); public static final BlockPos ZERO = new BlockPos(0, 0, 0); - private static final int PACKED_X_LENGTH = 1 + Mth.log2(Mth.smallestEncompassingPowerOfTwo(30000000)); -- private static final int PACKED_Z_LENGTH = BlockPos.PACKED_X_LENGTH; -- private static final int PACKED_Y_LENGTH = 64 - BlockPos.PACKED_X_LENGTH - BlockPos.PACKED_Z_LENGTH; -- private static final long PACKED_X_MASK = (1L << BlockPos.PACKED_X_LENGTH) - 1L; -- private static final long PACKED_Y_MASK = (1L << BlockPos.PACKED_Y_LENGTH) - 1L; -- private static final long PACKED_Z_MASK = (1L << BlockPos.PACKED_Z_LENGTH) - 1L; -- private static final int Z_OFFSET = BlockPos.PACKED_Y_LENGTH; -- private static final int X_OFFSET = BlockPos.PACKED_Y_LENGTH + BlockPos.PACKED_Z_LENGTH; +- private static final int PACKED_Z_LENGTH = PACKED_X_LENGTH; +- public static final int PACKED_Y_LENGTH = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH; +- private static final long PACKED_X_MASK = (1L << PACKED_X_LENGTH) - 1L; +- private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L; +- private static final long PACKED_Z_MASK = (1L << PACKED_Z_LENGTH) - 1L; +- private static final int Y_OFFSET = 0; +- private static final int Z_OFFSET = PACKED_Y_LENGTH; +- private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_Z_LENGTH; + // Paper start - static constants + private static final int PACKED_X_LENGTH = 26; + private static final int PACKED_Z_LENGTH = 26; -+ private static final int PACKED_Y_LENGTH = 12; ++ public static final int PACKED_Y_LENGTH = 12; + private static final long PACKED_X_MASK = 67108863; + private static final long PACKED_Y_MASK = 4095; + private static final long PACKED_Z_MASK = 67108863; @@ -50,17 +51,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public static int getX(long packedPos) { -- return (int) (packedPos << 64 - BlockPos.X_OFFSET - BlockPos.PACKED_X_LENGTH >> 64 - BlockPos.PACKED_X_LENGTH); +- return (int)(packedPos << 64 - X_OFFSET - PACKED_X_LENGTH >> 64 - PACKED_X_LENGTH); + return (int) (packedPos >> 38); // Paper - simplify/inline } public static int getY(long packedPos) { -- return (int) (packedPos << 64 - BlockPos.PACKED_Y_LENGTH >> 64 - BlockPos.PACKED_Y_LENGTH); +- return (int)(packedPos << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH); + return (int) ((packedPos << 52) >> 52); // Paper - simplify/inline } public static int getZ(long packedPos) { -- return (int) (packedPos << 64 - BlockPos.Z_OFFSET - BlockPos.PACKED_Z_LENGTH >> 64 - BlockPos.PACKED_Z_LENGTH); +- return (int)(packedPos << 64 - Z_OFFSET - PACKED_Z_LENGTH >> 64 - PACKED_Z_LENGTH); + return (int) ((packedPos << 26) >> 38); // Paper - simplify/inline } @@ -71,15 +72,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public long asLong() { @@ -0,0 +0,0 @@ public class BlockPos extends Vec3i { + } - public static long asLong(int x, int y, int z) { return asLong(x, y, z); } // Paper - OBFHELPER public static long asLong(int x, int y, int z) { - long l = 0L; -- -- l |= ((long) x & BlockPos.PACKED_X_MASK) << BlockPos.X_OFFSET; -- l |= ((long) y & BlockPos.PACKED_Y_MASK) << 0; -- l |= ((long) z & BlockPos.PACKED_Z_MASK) << BlockPos.Z_OFFSET; -- return l; +- l = l | ((long)x & PACKED_X_MASK) << X_OFFSET; +- l = l | ((long)y & PACKED_Y_MASK) << 0; +- return l | ((long)z & PACKED_Z_MASK) << Z_OFFSET; + return (((long) x & (long) 67108863) << 38) | (((long) y & (long) 4095)) | (((long) z & (long) 67108863) << 12); // Paper - inline constants and simplify } @@ -105,7 +104,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return new SectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); // Paper } - public static long offset(long packed, Direction direction) { + public static SectionPos bottomOf(ChunkAccess chunk) { +@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i { return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ()); } @@ -122,7 +122,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return (((long) ((int) (packed >> 42) + x) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + y) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + z) & 4194303L) << 20); // Simplify to reduce instruction count } - public static int blockToSectionCoord(int coord) { + public static int posToSectionCoord(double coord) { @@ -0,0 +0,0 @@ public class SectionPos extends Vec3i { } @@ -130,8 +130,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - int i = sectionRelative(pos.getX()); - int j = sectionRelative(pos.getY()); - int k = sectionRelative(pos.getZ()); -- -- return (short) (i << 8 | k << 4 | j << 0); +- return (short)(i << 8 | k << 4 | j << 0); + return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline } @@ -141,29 +140,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } - public int minBlockX() { -- return this.x() << 4; -+ public final int minBlockX() { // Paper -+ return this.getX() << 4; // Paper +- return sectionToBlockCoord(this.x()); ++ public final int minBlockX() { // Paper - make final ++ return this.getX() << 4; // Paper - inline } - public int minBlockY() { -- return this.y() << 4; -+ public final int minBlockY() { // Paper -+ return this.getY() << 4; // Paper +- return sectionToBlockCoord(this.y()); ++ public final int minBlockY() { // Paper - make final ++ return this.getY() << 4; // Paper - inline } - public int minBlockZ() { -- return this.z() << 4; -+ public final int minBlockZ() { // Paper -+ return this.getZ() << 4; // Paper +- return sectionToBlockCoord(this.z()); ++ public int minBlockZ() { // Paper - make final ++ return this.getZ() << 4; // Paper - inline } public int maxBlockX() { @@ -0,0 +0,0 @@ public class SectionPos extends Vec3i { - return (this.z() << 4) + 15; } -+ public static long blockToSection(long i) { return blockToSection(i); } // Paper - OBFHELPER public static long blockToSection(long blockPos) { - return asLong(blockToSectionCoord(BlockPos.getX(blockPos)), blockToSectionCoord(BlockPos.getY(blockPos)), blockToSectionCoord(BlockPos.getZ(blockPos))); + // b(a(BlockPosition.b(i)), a(BlockPosition.c(i)), a(BlockPosition.d(i))); @@ -172,7 +169,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public static long getZeroNode(long pos) { @@ -0,0 +0,0 @@ public class SectionPos extends Vec3i { - return new ChunkPos(this.x(), this.z()); + return asLong(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ())); } + // Paper start @@ -180,14 +177,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return (((long) (i >> 4) & 4194303L) << 42) | (((long) (j >> 4) & 1048575L)) | (((long) (k >> 4) & 4194303L) << 20); + } + // Paper end -+ public static long asLong(int i, int j, int k) { return asLong(i, j, k); } // Paper - OBFHELPER ++ public static long asLong(int x, int y, int z) { - long l = 0L; -- -- l |= ((long) x & 4194303L) << 42; -- l |= ((long) y & 1048575L) << 0; -- l |= ((long) z & 4194303L) << 20; -- return l; +- l = l | ((long)x & 4194303L) << 42; +- l = l | ((long)y & 1048575L) << 0; +- return l | ((long)z & 4194303L) << 20; + return (((long) x & 4194303L) << 42) | (((long) y & 1048575L)) | (((long) z & 4194303L) << 20); // Paper - Simplify to reduce instruction count } @@ -196,25 +191,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return (((long) getX() & 4194303L) << 42) | (((long) getY() & 1048575L)) | (((long) getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count } - public Stream blocksInside() { + @Override @@ -0,0 +0,0 @@ public class SectionPos extends Vec3i { } public static Stream cube(SectionPos center, int radius) { -- int j = center.x(); -- int k = center.y(); -- int l = center.z(); -- -- return betweenClosedStream(j - radius, k - radius, l - radius, j + radius, k + radius, l + radius); +- int i = center.x(); +- int j = center.y(); +- int k = center.z(); +- return betweenClosedStream(i - radius, j - radius, k - radius, i + radius, j + radius, k + radius); + return betweenClosedStream(center.getX() - radius, center.getY() - radius, center.getZ() - radius, center.getX() + radius, center.getY() + radius, center.getZ() + radius); // Paper - simplify/inline } - public static Stream aroundChunk(ChunkPos center, int radius) { -- int j = center.x; -- int k = center.z; -- -- return betweenClosedStream(j - radius, 0, k - radius, j + radius, 15, k + radius); + public static Stream aroundChunk(ChunkPos center, int radius, int minY, int maxY) { +- int i = center.x; +- int j = center.z; +- return betweenClosedStream(i - radius, minY, j - radius, i + radius, maxY - 1, j + radius); + return betweenClosedStream(center.x - radius, 0, center.z - radius, center.x + radius, 15, center.z + radius); // Paper - simplify/inline } - public static Stream betweenClosedStream(final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ) { + public static Stream betweenClosedStream(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { diff --git a/patches/server-remapped/Optimize-sending-packets-to-nearby-locations-sounds-.patch b/patches/server/Optimize-sending-packets-to-nearby-locations-sounds-.patch similarity index 96% rename from patches/server-remapped/Optimize-sending-packets-to-nearby-locations-sounds-.patch rename to patches/server/Optimize-sending-packets-to-nearby-locations-sounds-.patch index cf60314936..c0cfeb1f02 100644 --- a/patches/server-remapped/Optimize-sending-packets-to-nearby-locations-sounds-.patch +++ b/patches/server/Optimize-sending-packets-to-nearby-locations-sounds-.patch @@ -32,7 +32,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (world == null) { + world = server.getLevel(worldKey); + } -+ ChunkMap chunkMap = world != null ? world.getChunkSource().chunkMap : null; ++ net.minecraft.server.level.ChunkMap chunkMap = world != null ? world.getChunkSource().chunkMap : null; + Object[] backingSet; + if (chunkMap == null) { + // Really shouldn't happen... diff --git a/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch new file mode 100644 index 0000000000..befe2c79c2 --- /dev/null +++ b/patches/server/Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 7 Jun 2020 19:25:13 -0400 +Subject: [PATCH] Use seed based lookup for Treasure Maps - Fixes lag from + carto/sunken maps + + +diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/item/MapItem.java ++++ b/src/main/java/net/minecraft/world/item/MapItem.java +@@ -0,0 +0,0 @@ public class MapItem extends ComplexItem { + + for (l = 0; l < 128 * i; ++l) { + for (i1 = 0; i1 < 128 * i; ++i1) { +- abiomebase[l * 128 * i + i1] = world.getBiome(new BlockPos((j / i - 64) * i + i1, 0, (k / i - 64) * i + l)); ++ abiomebase[l * 128 * i + i1] = world.getUncachedNoiseBiome((j / i - 64) * i + i1, 0, (k / i - 64) * i + l); // Paper + } + } + diff --git a/patches/server/implement-optional-per-player-mob-spawns.patch b/patches/server/implement-optional-per-player-mob-spawns.patch index 80ab388b52..2d8b8d5c80 100644 --- a/patches/server/implement-optional-per-player-mob-spawns.patch +++ b/patches/server/implement-optional-per-player-mob-spawns.patch @@ -779,6 +779,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 while (k < 3) { @@ -0,0 +0,0 @@ public final class NaturalSpawner { + // Paper start + Boolean doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); + if (doSpawning == null) { +- return; ++ return j; // Paper + } + if (doSpawning && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) { + // Paper end Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type); if (entityinsentient == null) {