2021-06-11 14:02:28 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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
2021-06-16 12:14:53 +02:00
index 18ae2e2b339d357fbe0f6f2b18bc14c0dfe4c222..3b7ba9c755c82a6f086d5542d32b3567c0f98b99 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
@@ -108,7 +108,7 @@ public final class ChunkTaskManager {
}
static void dumpChunkInfo(Set<ChunkHolder> seenChunks, ChunkHolder chunkHolder, int x, int z) {
- dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 1);
2021-06-16 12:14:53 +02:00
+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 4); // Paper - 1->4
2021-06-11 14:02:28 +02:00
}
static void dumpChunkInfo(Set<ChunkHolder> seenChunks, ChunkHolder chunkHolder, int x, int z, int indent, int maxDepth) {
2021-06-16 12:14:53 +02:00
@@ -129,6 +129,31 @@ public final class ChunkTaskManager {
2021-06-11 14:02:28 +02:00
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()));
2021-06-16 12:14:53 +02:00
+ // Paper start
+ PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Holder Priority - " + chunkHolder.queueLevel);
2021-06-11 14:02:28 +02:00
+
+ 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();
2021-06-16 12:14:53 +02:00
+ if (status != null && status.isOrAfter(ChunkHolder.getStatus(neighbor.getTicketLevel()))) {
2021-06-11 14:02:28 +02:00
+ 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);
+ }
+ }
2021-06-16 12:14:53 +02:00
+ // Paper end
2021-06-11 14:02:28 +02:00
}
}
2021-06-16 08:46:06 +02:00
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
2021-06-17 23:39:36 +02:00
index 2d5b8e35d52b0dfd075af81a3a936d8a21053b31..9ddedd310eb0323a5a09f51a61bfb7b36503be93 100644
2021-06-16 08:46:06 +02:00
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
2021-06-16 12:14:53 +02:00
@@ -671,6 +671,7 @@ public final class MCUtil {
chunkData.addProperty("x", playerChunk.pos.x);
chunkData.addProperty("z", playerChunk.pos.z);
2021-06-16 08:46:06 +02:00
chunkData.addProperty("ticket-level", playerChunk.getTicketLevel());
2021-06-16 12:14:53 +02:00
+ chunkData.addProperty("priority", playerChunk.queueLevel); // Paper - priority
2021-06-16 08:46:06 +02:00
chunkData.addProperty("state", ChunkHolder.getFullChunkStatus(playerChunk.getTicketLevel()).toString());
2021-06-16 12:14:53 +02:00
chunkData.addProperty("queued-for-unload", chunkMap.toDrop.contains(playerChunk.pos.longKey));
2021-06-16 08:46:06 +02:00
chunkData.addProperty("status", status == null ? "unloaded" : status.toString());
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
2021-06-24 11:12:47 +02:00
index e26b65c3b2594ae45b68bcbd135152130663be7f..3e004f391668865c0e6f1c38cef9661f7372fffe 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
2021-06-16 12:14:53 +02:00
@@ -60,7 +60,7 @@ public class ChunkHolder {
private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
2021-06-11 14:02:28 +02:00
public int oldTicketLevel;
private int ticketLevel;
- private int queueLevel;
2021-06-16 12:14:53 +02:00
+ public volatile int queueLevel; // Paper - private->public, make volatile since this is concurrently accessed
2021-06-16 19:48:25 +02:00
public final ChunkPos pos;
2021-06-11 14:02:28 +02:00
private boolean hasChangedSections;
private final ShortSet[] changedBlocksPerSection;
2021-06-16 12:14:53 +02:00
@@ -75,6 +75,7 @@ public class ChunkHolder {
2021-06-11 14:02:28 +02:00
2021-06-16 12:14:53 +02:00
boolean isUpdateQueued = false; // Paper
2021-06-11 14:02:28 +02:00
private final ChunkMap chunkMap; // Paper
+ public ServerLevel getWorld() { return chunkMap.level; } // Paper
2021-06-16 12:14:53 +02:00
// Paper start - no-tick view distance
public final LevelChunk getSendingChunk() {
// it's important that we use getChunkAtIfLoadedImmediately to mirror the chunk sending logic used
@@ -100,6 +101,134 @@ public class ChunkHolder {
2021-06-16 08:46:06 +02:00
}
2021-06-16 12:14:53 +02:00
// Paper end
2021-06-11 14:02:28 +02:00
+ // Paper start - Chunk gen/load priority system
+ volatile int neighborPriority = -1;
+ volatile int priorityBoost = 0;
+ public final java.util.concurrent.ConcurrentHashMap<ChunkHolder, ChunkStatus> neighbors = new java.util.concurrent.ConcurrentHashMap<>();
2021-06-16 12:14:53 +02:00
+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<Integer> neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
2021-06-11 14:02:28 +02:00
+
+ 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) -> {
2021-06-16 12:14:53 +02:00
+ if (currentWantedStatus == null || !currentWantedStatus.isOrAfter(status)) {
2021-06-11 14:02:28 +02:00
+ //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) -> {
2021-06-16 12:14:53 +02:00
+ if (wantedStatus != null && chunkstatus.isOrAfter(wantedStatus)) {
2021-06-11 14:02:28 +02:00
+ //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();
+ }
2021-06-16 08:46:06 +02:00
+
2021-06-11 14:02:28 +02:00
+ 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() {
2021-06-16 12:14:53 +02:00
+ if (queueLevel != getDemandedPriority()) this.chunkMap.queueHolderUpdate(this);
2021-06-11 14:02:28 +02:00
+ }
+
+ public final double getDistance(ServerPlayer player) {
+ return getDistance(player.getX(), player.getZ());
+ }
+ public final double getDistance(double blockX, double blockZ) {
2021-06-16 12:14:53 +02:00
+ int cx = net.minecraft.server.MCUtil.fastFloor(blockX) >> 4;
+ int cz = net.minecraft.server.MCUtil.fastFloor(blockZ) >> 4;
2021-06-11 14:02:28 +02:00
+ 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());
+ }
+
+ public static ChunkStatus getNextStatus(ChunkStatus status) {
+ if (status == ChunkStatus.FULL) {
2021-06-16 08:46:06 +02:00
+ return status;
+ }
2021-06-16 12:14:53 +02:00
+ return CHUNK_STATUSES.get(status.getIndex() + 1);
2021-06-11 14:02:28 +02:00
+ }
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) {
2021-06-16 08:46:06 +02:00
+ return ensureMain(getFutureIfPresentUnchecked(chunkstatus));
+ }
2021-06-11 14:02:28 +02:00
+ public <T> CompletableFuture<T> ensureMain(CompletableFuture<T> future) {
+ return future.thenApplyAsync(r -> r, chunkMap.mainInvokingExecutor);
+ }
2021-06-16 12:14:53 +02:00
+
+ @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 " + queueLevel +
+ '}';
+ }
+ // Paper end
+
// Paper start - optimise isOutsideOfRange
// cached here to avoid a map lookup
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
2021-06-17 23:39:36 +02:00
@@ -485,7 +614,7 @@ public class ChunkHolder {
2021-06-11 14:02:28 +02:00
// 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(() -> {
2021-06-24 11:12:47 +02:00
@@ -544,13 +673,14 @@ public class ChunkHolder {
2021-06-16 12:14:53 +02:00
this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
this.scheduleFullChunkPromotion(chunkStorage, this.fullChunkFuture, executor, ChunkHolder.FullChunkStatus.BORDER);
2021-06-11 14:02:28 +02:00
// Paper start - cache ticking ready status
2021-06-16 12:14:53 +02:00
- this.fullChunkFuture.thenAccept(either -> {
+ ensureMain(this.fullChunkFuture).thenAccept(either -> { // Paper - ensure main
final Optional<LevelChunk> left = either.left();
if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) {
2021-06-11 14:02:28 +02:00
// 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;
2021-06-16 08:46:06 +02:00
+ this.chunkMap.distanceManager.clearPriorityTickets(pos);
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
});
this.updateChunkToSave(this.fullChunkFuture, "full");
2021-06-24 11:12:47 +02:00
@@ -574,7 +704,7 @@ public class ChunkHolder {
2021-06-16 12:14:53 +02:00
this.tickingChunkFuture = chunkStorage.prepareTickingChunk(this);
this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, ChunkHolder.FullChunkStatus.TICKING);
2021-06-11 14:02:28 +02:00
// Paper start - cache ticking ready status
2021-06-16 12:14:53 +02:00
- this.tickingChunkFuture.thenAccept(either -> {
+ ensureMain(this.tickingChunkFuture).thenAccept(either -> { // Paper - ensure main
either.ifLeft(chunk -> {
2021-06-11 14:02:28 +02:00
// note: Here is a very good place to add callbacks to logic waiting on this.
2021-06-16 12:14:53 +02:00
ChunkHolder.this.isTickingReady = true;
2021-06-24 11:12:47 +02:00
@@ -604,7 +734,7 @@ public class ChunkHolder {
2021-06-16 12:14:53 +02:00
this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos);
this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, ChunkHolder.FullChunkStatus.ENTITY_TICKING);
2021-06-11 14:02:28 +02:00
// Paper start - cache ticking ready status
2021-06-16 12:14:53 +02:00
- this.entityTickingChunkFuture.thenAccept(either -> {
+ ensureMain(this.entityTickingChunkFuture).thenAccept(either -> { // Paper ensureMain
either.ifLeft(chunk -> {
ChunkHolder.this.isEntityTickingReady = true;
});
2021-06-24 11:12:47 +02:00
@@ -622,12 +752,30 @@ public class ChunkHolder {
2021-06-16 12:14:53 +02:00
this.demoteFullChunk(chunkStorage, playerchunk_state1);
2021-06-11 14:02:28 +02:00
}
2021-06-16 08:46:06 +02:00
- this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
2021-06-16 12:14:53 +02:00
+ //this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
2021-06-11 14:02:28 +02:00
+ // Paper start - raise IO/load priority if priority changes, use our preferred priority
2021-06-16 08:46:06 +02:00
+ priorityBoost = chunkMap.distanceManager.getChunkPriority(pos);
2021-06-11 14:02:28 +02:00
+ int priority = getDemandedPriority();
2021-06-16 12:14:53 +02:00
+ if (this.queueLevel > priority) {
2021-06-16 08:46:06 +02:00
+ int ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY;
2021-06-11 14:02:28 +02:00
+ if (priority <= 10) {
2021-06-16 08:46:06 +02:00
+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY;
2021-06-11 14:02:28 +02:00
+ } else if (priority <= 20) {
2021-06-16 08:46:06 +02:00
+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY;
2021-06-11 14:02:28 +02:00
+ }
+ chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority);
+ }
2021-06-16 12:14:53 +02:00
+ if (this.queueLevel != priority) {
+ this.onLevelChange.onLevelChange(this.pos, () -> this.queueLevel, priority, p -> this.queueLevel = p); // use preferred priority
2021-06-11 14:02:28 +02:00
+ 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) -> {
2021-06-16 08:46:06 +02:00
+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
2021-06-11 14:02:28 +02:00
LevelChunk chunk = (LevelChunk)either.left().orElse(null);
if (chunk != null) {
chunkStorage.callbackExecutor.execute(() -> {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
2021-07-07 08:52:40 +02:00
index f518fc7a39a807bff2e141fd238ab1bfc34ce890..dd0f9ab476735e0a61533bdfed5868fa5f523b68 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
2021-06-17 10:37:27 +02:00
@@ -149,6 +149,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
public final ServerLevel level;
private final ThreadedLevelLightEngine lightEngine;
private final BlockableEventLoop<Runnable> mainThreadExecutor;
+ final java.util.concurrent.Executor mainInvokingExecutor; // Paper
public final ChunkGenerator generator;
2021-06-17 19:11:00 +02:00
public final Supplier<DimensionDataStorage> overworldDataStorage;
2021-06-11 14:02:28 +02:00
private final PoiManager poiManager;
2021-06-17 10:37:27 +02:00
@@ -187,6 +188,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
@Override
public void execute(Runnable runnable) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("Callback Executor execute");
if (this.queue == null) {
this.queue = new java.util.ArrayDeque<>();
2021-06-11 14:02:28 +02:00
}
2021-06-17 10:37:27 +02:00
@@ -195,6 +197,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
@Override
public void run() {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("Callback Executor run");
if (this.queue == null) {
2021-06-11 14:02:28 +02:00
return;
}
2021-07-07 08:52:40 +02:00
@@ -353,6 +356,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-16 12:14:53 +02:00
this.level = world;
2021-06-11 14:02:28 +02:00
this.generator = chunkGenerator;
this.mainThreadExecutor = mainThreadExecutor;
+ // Paper start
+ this.mainInvokingExecutor = (run) -> {
+ if (MCUtil.isMainThread()) {
+ run.run();
+ } else {
+ mainThreadExecutor.execute(run);
+ }
+ };
+ // Paper end
2021-06-16 12:14:53 +02:00
ProcessorMailbox<Runnable> threadedmailbox = ProcessorMailbox.create(executor, "worldgen");
2021-06-11 14:02:28 +02:00
2021-06-16 12:14:53 +02:00
Objects.requireNonNull(mainThreadExecutor);
2021-07-07 08:52:40 +02:00
@@ -448,6 +460,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
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<ServerPlayer> newState) -> {
+ checkHighPriorityChunks(player);
if (newState.size() != 1) {
return;
}
2021-07-07 08:52:40 +02:00
@@ -466,7 +479,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
}
ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ);
ChunkMap.this.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
- });
2021-06-16 12:14:53 +02:00
+ // Paper start
2021-06-11 14:02:28 +02:00
+ ChunkMap.this.level.getChunkSource().clearPriorityTickets(chunkPos);
2021-06-16 12:14:53 +02:00
+ },
+ (player, prevPos, newPos) -> {
2021-06-11 14:02:28 +02:00
+ player.lastHighPriorityChecked = -1; // reset and recheck
+ checkHighPriorityChunks(player);
+ });
2021-06-16 12:14:53 +02:00
+ // Paper end
2021-06-11 14:02:28 +02:00
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,
2021-07-07 08:52:40 +02:00
@@ -484,6 +504,116 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
// Paper end - no-tick view distance
}
2021-06-16 12:14:53 +02:00
2021-06-11 14:02:28 +02:00
+ // 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);
+ }
+ }
2021-06-16 08:46:06 +02:00
+
2021-06-11 14:02:28 +02:00
+ private boolean isUnloading(ChunkHolder playerchunk) {
+ return playerchunk == null || toDrop.contains(playerchunk.pos.toLong());
+ }
+
2021-06-16 12:14:53 +02:00
+ private void updateChunkPriorityMap(it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap map, long chunk, int level) {
2021-06-11 14:02:28 +02:00
+ 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;
2021-06-16 12:14:53 +02:00
+ it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap priorities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap();
2021-06-11 14:02:28 +02:00
+
+ int viewDistance = getEffectiveNoTickViewDistance();
2021-06-16 12:14:53 +02:00
+ net.minecraft.core.BlockPos.MutableBlockPos pos = new net.minecraft.core.BlockPos.MutableBlockPos();
2021-06-11 14:02:28 +02:00
+
+ // Prioritize circular near
+ double playerChunkX = Mth.floor(player.getX()) >> 4;
+ double playerChunkZ = Mth.floor(player.getZ()) >> 4;
2021-06-17 23:39:36 +02:00
+ pos.set(player.getX(), 0, player.getZ());
2021-06-11 14:02:28 +02:00
+ 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);
2021-06-17 23:39:36 +02:00
+ pos.set(front3.x << 4, 0, front3.z << 4);
2021-06-11 14:02:28 +02:00
+ 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);
2021-06-17 23:39:36 +02:00
+ pos.set(front5.x << 4, 0, front5.z << 4);
2021-06-11 14:02:28 +02:00
+ 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);
2021-06-17 23:39:36 +02:00
+ pos.set(front7.x << 4, 0, front7.z << 4);
2021-06-11 14:02:28 +02:00
+ 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
2021-06-16 12:14:53 +02:00
+
// Paper start
2021-06-11 14:02:28 +02:00
public void updatePlayerMobTypeMap(Entity entity) {
if (!this.level.paperConfig.perPlayerMobSpawns) {
2021-07-07 08:52:40 +02:00
@@ -642,6 +772,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
List<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> 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) {
2021-07-07 08:52:40 +02:00
@@ -660,6 +791,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
ChunkStatus chunkstatus = (ChunkStatus) distanceToStatus.apply(j1);
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> 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);
}
2021-07-07 08:52:40 +02:00
@@ -1103,14 +1242,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
};
CompletableFuture<CompoundTag> chunkSaveFuture = this.level.asyncChunkTaskManager.getChunkSaveFuture(pos.x, pos.z);
2021-06-16 12:14:53 +02:00
+ // Paper start
2021-06-11 14:02:28 +02:00
+ ChunkHolder playerChunk = getUpdatingChunkIfPresent(pos.toLong());
2021-06-16 12:14:53 +02:00
+ int chunkPriority = playerChunk != null ? playerChunk.queueLevel : 33;
2021-06-11 14:02:28 +02:00
+ 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;
2021-06-16 12:14:53 +02:00
+ // Paper end
2021-06-11 14:02:28 +02:00
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);
2021-06-16 12:14:53 +02:00
+ this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, priority, chunkHolderConsumer, isHighestPriority, chunkSaveFuture); // Paper
2021-06-11 14:02:28 +02:00
} else {
- this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z,
- com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false);
2021-06-16 12:14:53 +02:00
+ this.level.asyncChunkTaskManager.scheduleChunkLoad(pos.x, pos.z, priority, chunkHolderConsumer, isHighestPriority); // Paper
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
+ this.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, priority); // Paper
2021-06-11 14:02:28 +02:00
return ret;
// Paper end
}
2021-07-07 08:52:40 +02:00
@@ -1242,7 +1391,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
2021-06-11 14:02:28 +02:00
long i = playerchunk.getPos().toLong();
2021-06-16 12:14:53 +02:00
Objects.requireNonNull(playerchunk);
2021-06-11 14:02:28 +02:00
- 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
2021-06-17 23:39:36 +02:00
index d94241bcca4f2fd5e464a860bd356af504dc68b7..1cc4e0a1f3d8235ef88b48e01ca8b78a263d2676 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
2021-06-16 12:14:53 +02:00
@@ -98,6 +98,7 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
}
private static int getTicketLevelAt(SortedArraySet<Ticket<?>> arraysetsorted) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::getLowestTicketLevel"); // Paper
2021-06-11 14:02:28 +02:00
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.first()).getTicketLevel() : ChunkMap.MAX_CHUNK_DISTANCE + 1;
}
2021-06-16 12:14:53 +02:00
@@ -111,6 +112,7 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
2021-06-16 12:14:53 +02:00
public boolean runAllUpdates(ChunkMap playerchunkmap) {
2021-06-11 14:02:28 +02:00
//this.f.a(); // Paper - no longer used
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
2021-06-11 14:02:28 +02:00
this.playerTicketManager.runAllUpdates();
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
boolean flag = i != 0;
2021-06-16 12:14:53 +02:00
@@ -121,11 +123,13 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
// Paper start
if (!this.pendingChunkUpdates.isEmpty()) {
2021-06-16 12:14:53 +02:00
+ this.pollingPendingChunkUpdates = true; try { // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
while(!this.pendingChunkUpdates.isEmpty()) {
ChunkHolder remove = this.pendingChunkUpdates.remove();
remove.isUpdateQueued = false;
2021-06-16 12:14:53 +02:00
remove.updateFutures(playerchunkmap, this.mainThreadExecutor);
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
+ } finally { this.pollingPendingChunkUpdates = false; } // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
// Paper end
return true;
} else {
2021-06-16 12:14:53 +02:00
@@ -161,8 +165,10 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
return flag;
}
}
2021-06-16 12:14:53 +02:00
+ boolean pollingPendingChunkUpdates = false; // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
2021-06-16 12:14:53 +02:00
boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper
2021-06-11 14:02:28 +02:00
SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
2021-06-16 12:14:53 +02:00
int j = DistanceManager.getTicketLevelAt(arraysetsorted);
2021-06-11 14:02:28 +02:00
Ticket<?> ticket1 = (Ticket) arraysetsorted.addOrGet(ticket); // CraftBukkit - decompile error
2021-06-16 12:14:53 +02:00
@@ -176,7 +182,9 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
2021-06-11 14:02:28 +02:00
SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
+ int oldLevel = getTicketLevelAt(arraysetsorted); // Paper
boolean removed = false; // CraftBukkit
if (arraysetsorted.remove(ticket)) {
2021-06-16 12:14:53 +02:00
@@ -208,7 +216,12 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
this.tickets.remove(i);
}
2021-06-16 12:14:53 +02:00
- this.ticketTracker.update(i, DistanceManager.getTicketLevelAt(arraysetsorted), false);
+ // Paper start - Chunk priority
+ int newLevel = getTicketLevelAt(arraysetsorted);
+ if (newLevel > oldLevel) {
+ this.ticketTracker.update(i, newLevel, false);
+ }
+ // Paper end
2021-06-11 14:02:28 +02:00
return removed; // CraftBukkit
}
2021-06-16 12:14:53 +02:00
@@ -250,6 +263,136 @@ public abstract class DistanceManager {
});
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
2021-06-11 14:02:28 +02:00
+ 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;
2021-06-16 12:14:53 +02:00
+ net.minecraft.server.MCUtil.getSpiralOutChunks(center.getWorldPosition(), radius).forEach(coords -> {
2021-06-11 14:02:28 +02:00
+ addPriorityTicket(coords, TicketType.PRIORITY, finalPriority);
+ });
+ delayDistanceManagerTick = false;
+ chunkMap.level.getChunkSource().runDistanceManagerUpdates();
+ }
+
+ public void clearAreaPriorityTickets(ChunkPos center, int radius) {
+ delayDistanceManagerTick = true;
2021-06-16 12:14:53 +02:00
+ net.minecraft.server.MCUtil.getSpiralOutChunks(center.getWorldPosition(), radius).forEach(coords -> {
2021-06-11 14:02:28 +02:00
+ this.removeTicket(coords.toLong(), new Ticket<ChunkPos>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
+ });
+ delayDistanceManagerTick = false;
+ chunkMap.level.getChunkSource().runDistanceManagerUpdates();
+ }
+
+ private boolean hasPlayerTicket(ChunkPos coords, int level) {
+ SortedArraySet<Ticket<?>> 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<ChunkPos> ticketType, int priority) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket");
2021-06-11 14:02:28 +02:00
+ 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);
2021-06-16 12:14:53 +02:00
+ this.ticketsToRelease.add(pair);
2021-06-11 14:02:28 +02:00
+ 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<ChunkPos> ticket = new Ticket<ChunkPos>(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<ChunkPos> type, int priority) {
+ SortedArraySet<Ticket<?>> 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
2021-06-16 12:14:53 +02:00
+ ticket.setCreatedTick(this.ticketTickCounter);
2021-06-11 14:02:28 +02:00
+ ticket.priority = Math.max(ticket.priority, priority);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public int getChunkPriority(ChunkPos coords) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::getChunkPriority");
2021-06-11 14:02:28 +02:00
+ SortedArraySet<Ticket<?>> 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) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::clearPriority");
2021-06-11 14:02:28 +02:00
+ this.removeTicket(coords.toLong(), new Ticket<ChunkPos>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
+ }
+
+ public void clearUrgent(ChunkPos coords) {
2021-06-16 12:14:53 +02:00
+ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::clearUrgent");
2021-06-11 14:02:28 +02:00
+ this.removeTicket(coords.toLong(), new Ticket<ChunkPos>(TicketType.URGENT, PRIORITY_TICKET_LEVEL, coords));
+ }
+ // Paper end
2021-06-16 12:14:53 +02:00
+
protected void updateChunkForced(ChunkPos pos, boolean forced) {
Ticket<ChunkPos> ticket = new Ticket<>(TicketType.FORCED, 31, pos);
2021-06-16 08:46:06 +02:00
2021-06-16 12:14:53 +02:00
@@ -516,41 +659,68 @@ public abstract class DistanceManager {
2021-06-11 14:02:28 +02:00
public void updateViewDistance(int watchDistance) {
ObjectIterator objectiterator = this.chunks.long2ByteEntrySet().iterator();
+ // Paper start - set the view distance before scheduling chunk loads/unloads
2021-06-16 12:14:53 +02:00
+ int lastViewDistance = this.viewDistance;
+ this.viewDistance = watchDistance;
2021-06-11 14:02:28 +02:00
+ // Paper end
while (objectiterator.hasNext()) {
2021-06-16 12:14:53 +02:00
it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry it_unimi_dsi_fastutil_longs_long2bytemap_entry = (it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry) objectiterator.next();
2021-06-11 14:02:28 +02:00
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;
2021-06-16 12:14:53 +02:00
+ // this.viewDistance = watchDistance; // Paper - view distance is now set further up
2021-06-11 14:02:28 +02:00
}
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
2021-06-16 12:14:53 +02:00
+ ChunkPos coords = new ChunkPos(pos); // Paper - reuse variable
2021-06-11 14:02:28 +02:00
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords); // Paper - no-tick view distance
if (withinViewDistance) {
2021-06-16 12:14:53 +02:00
+ scheduleChunkLoad(pos, net.minecraft.server.MinecraftServer.currentTick, distance, (priority) -> { // Paper - smarter ticket delay based on frustum and distance
2021-06-11 14:02:28 +02:00
+ // 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
2021-06-16 12:14:53 +02:00
DistanceManager.this.ticketThrottlerInput.tell(ChunkTaskPriorityQueueSorter.message(() -> {
2021-06-11 14:02:28 +02:00
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 {
+ }} else { // Paper
2021-06-16 12:14:53 +02:00
DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> {
2021-06-11 14:02:28 +02:00
}, pos, false));
}
});
}, pos, () -> {
- return distance;
2021-06-16 12:14:53 +02:00
+ return Math.min(ChunkMap.MAX_CHUNK_DISTANCE, priority); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
}));
+ }); // Paper
} else {
DistanceManager.this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> {
DistanceManager.this.mainThreadExecutor.execute(() -> {
DistanceManager.this.removeTicket(pos, ticket);
2021-06-16 12:14:53 +02:00
+ DistanceManager.this.clearPriorityTickets(coords); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
});
}, pos, true));
}
2021-06-16 12:14:53 +02:00
@@ -592,5 +762,100 @@ public abstract class DistanceManager {
private boolean haveTicketFor(int distance) {
return distance <= this.viewDistance - 2;
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
+
2021-06-11 14:02:28 +02:00
+ // Paper start - smart scheduling of player tickets
+ private boolean isChunkInRange(long i) {
2021-06-16 12:14:53 +02:00
+ return this.haveTicketFor(this.getLevel(i));
2021-06-11 14:02:28 +02:00
+ }
+ public void scheduleChunkLoad(long i, long startTick, int initialDistance, java.util.function.Consumer<Integer> task) {
2021-06-16 12:14:53 +02:00
+ long elapsed = net.minecraft.server.MinecraftServer.currentTick - startTick;
2021-06-11 14:02:28 +02:00
+ 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<ServerPlayer> 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();
+
2021-06-16 12:14:53 +02:00
+ net.minecraft.core.BlockPos blockPos = chunkPos.getWorldPosition();
2021-06-11 14:02:28 +02:00
+
+ boolean isFront = false;
2021-06-16 12:14:53 +02:00
+ net.minecraft.core.BlockPos.MutableBlockPos pos = new net.minecraft.core.BlockPos.MutableBlockPos();
2021-06-11 14:02:28 +02:00
+ 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);
2021-06-17 23:39:36 +02:00
+ pos.set(pointInFront.x << 4, 0, pointInFront.z << 4);
2021-06-16 12:14:53 +02:00
+ double frontDist = net.minecraft.server.MCUtil.distanceSq(pos, blockPos);
2021-06-11 14:02:28 +02:00
+
2021-06-17 23:39:36 +02:00
+ pos.set(player.getX(), 0, player.getZ());
2021-06-16 12:14:53 +02:00
+ double center = net.minecraft.server.MCUtil.distanceSq(pos, blockPos);
2021-06-11 14:02:28 +02:00
+
+ double dist = Math.min(frontDist, center);
+ if (!isFront) {
+ ChunkPos pointInBack = player.getChunkInFront(-7);
2021-06-17 23:39:36 +02:00
+ pos.set(pointInBack.x << 4, 0, pointInBack.z << 4);
2021-06-16 12:14:53 +02:00
+ double backDist = net.minecraft.server.MCUtil.distanceSq(pos, blockPos);
2021-06-11 14:02:28 +02:00
+ 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;
2021-06-16 12:14:53 +02:00
+ if (current != null && current.isOrAfter(ChunkStatus.LIGHT)) {
2021-06-11 14:02:28 +02:00
+ 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));
2021-06-16 12:14:53 +02:00
+ net.minecraft.server.MCUtil.scheduleTask(taskDelay, () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer");
2021-06-11 14:02:28 +02:00
+ }
+ }
+ // Paper end
2021-06-16 12:14:53 +02:00
}
}
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
2021-07-16 18:18:04 +02:00
index c28eda68d744c0a1c9229790af3a89dfac94ae22..f72ef5a0f97119732babc0ea353658cef52d6fe0 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
2021-07-16 18:18:04 +02:00
@@ -446,6 +446,26 @@ public class ServerChunkCache extends ChunkSource {
2021-06-11 14:02:28 +02:00
public <T> void removeTicketAtLevel(TicketType<T> 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);
+ }
2021-06-16 12:14:53 +02:00
// Paper end - async chunk io
2021-06-11 14:02:28 +02:00
@Nullable
2021-07-16 18:18:04 +02:00
@@ -486,6 +506,8 @@ public class ServerChunkCache extends ChunkSource {
2021-06-16 12:14:53 +02:00
Objects.requireNonNull(completablefuture);
2021-06-11 14:02:28 +02:00
if (!completablefuture.isDone()) { // Paper
// Paper start - async chunk io/loading
2021-06-16 12:14:53 +02:00
+ ChunkPos pair = new ChunkPos(x1, z1); // Paper - Chunk priority
+ this.distanceManager.markUrgent(pair); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
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
2021-07-16 18:18:04 +02:00
@@ -494,6 +516,8 @@ public class ServerChunkCache extends ChunkSource {
2021-06-16 12:14:53 +02:00
chunkproviderserver_a.managedBlock(completablefuture::isDone);
2021-06-11 14:02:28 +02:00
com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug
this.level.timings.syncChunkLoad.stopTiming(); // Paper
2021-06-16 12:14:53 +02:00
+ this.distanceManager.clearPriorityTickets(pair); // Paper - Chunk priority
+ this.distanceManager.clearUrgent(pair); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
} // Paper
ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
return ichunkaccess1;
2021-07-16 18:18:04 +02:00
@@ -567,10 +591,12 @@ public class ServerChunkCache extends ChunkSource {
2021-06-11 14:02:28 +02:00
if (flag && !currentlyUnloading) {
// CraftBukkit end
this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
2021-06-16 12:14:53 +02:00
+ if (isUrgent) this.distanceManager.markUrgent(chunkcoordintpair); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
if (this.chunkAbsent(playerchunk, l)) {
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
gameprofilerfiller.push("chunkLoad");
2021-06-16 12:14:53 +02:00
+ distanceManager.delayDistanceManagerTick = false; // Paper - Chunk priority - ensure this is never false
2021-06-11 14:02:28 +02:00
this.runDistanceManagerUpdates();
playerchunk = this.getVisibleChunkIfPresent(k);
gameprofilerfiller.pop();
2021-07-16 18:18:04 +02:00
@@ -579,8 +605,13 @@ public class ServerChunkCache extends ChunkSource {
2021-06-11 14:02:28 +02:00
}
}
}
-
- return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(chunkstatus, this.chunkMap);
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
2021-06-11 14:02:28 +02:00
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> 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) {
2021-07-16 18:18:04 +02:00
@@ -632,6 +663,7 @@ public class ServerChunkCache extends ChunkSource {
2021-06-11 14:02:28 +02:00
}
2021-06-16 12:14:53 +02:00
public boolean runDistanceManagerUpdates() {
+ if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
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
2021-07-07 08:52:40 +02:00
index 9fab262e420ca693778c6d41a0cf7b2783875ae2..ca83d167b12422f4d7490d2ebdff401200444d42 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
2021-07-07 08:52:40 +02:00
@@ -189,6 +189,14 @@ public class ServerPlayer extends Player {
2021-06-11 14:02:28 +02:00
private int lastRecordedArmor = Integer.MIN_VALUE;
private int lastRecordedLevel = Integer.MIN_VALUE;
private int lastRecordedExperience = Integer.MIN_VALUE;
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
+ public long lastHighPriorityChecked;
2021-06-11 14:02:28 +02:00
+ public void forceCheckHighPriority() {
+ lastHighPriorityChecked = -1;
+ getLevel().getChunkSource().chunkMap.checkHighPriorityChunks(this);
+ }
2021-06-16 12:14:53 +02:00
+ public boolean isRealPlayer;
+ // Paper end
2021-06-11 14:02:28 +02:00
private float lastSentHealth = -1.0E8F;
private int lastSentFood = -99999999;
private boolean lastFoodSaturationZero = true;
2021-07-07 08:52:40 +02:00
@@ -325,6 +333,21 @@ public class ServerPlayer extends Player {
2021-06-11 14:02:28 +02:00
this.maxHealthCache = this.getMaxHealth();
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
}
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
2021-06-11 14:02:28 +02:00
+ public BlockPos getPointInFront(double inFront) {
2021-06-16 12:14:53 +02:00
+ double rads = Math.toRadians(net.minecraft.server.MCUtil.normalizeYaw(this.yRot + 90)); // MC rotates yaw 90 for some odd reason
2021-06-11 14:02:28 +02:00
+ 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) {
2021-06-16 12:14:53 +02:00
+ double rads = Math.toRadians(net.minecraft.server.MCUtil.normalizeYaw(this.yRot + 90)); // MC rotates yaw 90 for some odd reason
2021-06-11 14:02:28 +02:00
+ 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
2021-07-07 08:52:40 +02:00
@@ -646,6 +669,7 @@ public class ServerPlayer extends Player {
2021-06-16 12:14:53 +02:00
if (valid && !this.isSpectator() || !this.touchingUnloadedChunk()) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
2021-06-11 14:02:28 +02:00
super.tick();
}
2021-06-16 12:14:53 +02:00
+ if (valid && isAlive() && connection != null) ((ServerLevel)level).getChunkSource().chunkMap.checkHighPriorityChunks(this); // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
2021-06-16 12:14:53 +02:00
for (int i = 0; i < this.getInventory().getContainerSize(); ++i) {
ItemStack itemstack = this.getInventory().getItem(i);
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java
2021-06-16 19:48:25 +02:00
index f1128f0d4a9a0241ac6c9bc18dd13b431c616bb1..2b2b7851d5f68bcdb41d58bcc64740ba58bf1ef4 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/Ticket.java
+++ b/src/main/java/net/minecraft/server/level/Ticket.java
@@ -8,6 +8,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
2021-06-16 19:48:25 +02:00
public final T key;
public long createdTick;
2021-06-16 12:14:53 +02:00
public long delayUnloadBy; // Paper
+ public int priority; // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
protected Ticket(TicketType<T> type, int level, T argument) {
this.type = type;
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
2021-06-16 12:14:53 +02:00
index 8770fe0db46b01e8b608637df4f1a669a3f4cdde..3c1698ba0d3bc412ab957777d9b5211dbc555208 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/TicketType.java
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
2021-06-16 12:14:53 +02:00
@@ -9,6 +9,8 @@ import net.minecraft.world.level.ChunkPos;
public class TicketType<T> {
2021-06-11 14:02:28 +02:00
public static final TicketType<Long> FUTURE_AWAIT = create("future_await", Long::compareTo); // Paper
public static final TicketType<Long> ASYNC_LOAD = create("async_load", Long::compareTo); // Paper
+ public static final TicketType<ChunkPos> PRIORITY = create("priority", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper
+ public static final TicketType<ChunkPos> URGENT = create("urgent", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper
2021-06-16 12:14:53 +02:00
private final String name;
private final Comparator<T> comparator;
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
2021-07-07 08:52:40 +02:00
index d182b6ef4431c5ca291619bc8920a18cccad6327..aec4d7987ed168bc6a70c178d44b8d0d1822b845 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
2021-07-07 08:52:40 +02:00
@@ -1545,6 +1545,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
2021-06-11 14:02:28 +02:00
this.awaitingTeleportTime = this.tickCount;
this.player.absMoveTo(d0, d1, d2, f, f1);
+ this.player.forceCheckHighPriority(); // Paper
2021-06-16 12:14:53 +02:00
this.player.connection.send(new ClientboundPlayerPositionPacket(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport, flag));
2021-06-11 14:02:28 +02:00
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
2021-07-07 08:52:40 +02:00
index e0c9857a922d8a3f421d7df0f056b82c4775390e..4918f89401cdf6a85d8b71f3e3a6608239a388bd 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
2021-07-07 08:52:40 +02:00
@@ -286,8 +286,8 @@ public abstract class PlayerList {
2021-06-16 12:14:53 +02:00
net.minecraft.server.level.ChunkMap playerChunkMap = worldserver1.getChunkSource().chunkMap;
net.minecraft.server.level.DistanceManager distanceManager = playerChunkMap.distanceManager;
distanceManager.addTicketAtLevel(net.minecraft.server.level.TicketType.LOGIN, pos, 31, pos.toLong());
2021-06-11 14:02:28 +02:00
- worldserver1.getChunkSource().runDistanceManagerUpdates();
- worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
2021-06-16 12:14:53 +02:00
+ worldserver1.getChunkSource().markAreaHighPriority(pos, 28, 3); // Paper - Chunk priority
+ worldserver1.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, false).thenApply(chunk -> { // Paper - Chunk priority
net.minecraft.server.level.ChunkHolder updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong());
2021-06-11 14:02:28 +02:00
if (updatingChunk != null) {
2021-06-17 23:39:36 +02:00
return updatingChunk.getEntityTickingChunkFuture();
2021-07-07 08:52:40 +02:00
@@ -705,6 +705,7 @@ public abstract class PlayerList {
2021-06-11 14:02:28 +02:00
SocketAddress socketaddress = loginlistener.connection.getRemoteAddress();
2021-06-16 12:14:53 +02:00
ServerPlayer entity = new ServerPlayer(this.server, this.server.getLevel(Level.OVERWORLD), gameprofile);
+ entity.isRealPlayer = true; // Paper - Chunk priority
2021-06-11 14:02:28 +02:00
Player player = entity.getBukkitEntity();
PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.getRawAddress()).getAddress());
2021-07-07 08:52:40 +02:00
@@ -893,6 +894,7 @@ public abstract class PlayerList {
2021-06-11 14:02:28 +02:00
// CraftBukkit end
2021-06-16 12:14:53 +02:00
worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
+ entityplayer1.forceCheckHighPriority(); // Player - Chunk priority
while (avoidSuffocation && !worldserver1.noCollision(entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
2021-06-11 14:02:28 +02:00
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
}
2021-06-16 12:14:53 +02:00
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
2021-07-12 21:42:17 +02:00
index 21a3c5fe4cf0ac4f21ffda3d7c0b20f82d4cadf1..2a5f58a87cfe312d2118c1b6ba4df98b046c4db1 100644
2021-06-16 12:14:53 +02:00
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -223,7 +223,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
private Vec3 position;
private BlockPos blockPosition;
private Vec3 deltaMovement;
- private float yRot;
+ public float yRot; // Paper - private->public
private float xRot;
public float yRotO;
public float xRotO;
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
2021-06-16 12:14:53 +02:00
index f72471ac82907a0d5112598b3289689495285944..6e1f8323d028790d1f55d51edb3214d0161a0072 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
2021-06-16 12:14:53 +02:00
@@ -2558,6 +2558,12 @@ public class CraftWorld implements World {
2021-06-11 14:02:28 +02:00
return future;
}
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
2021-06-11 14:02:28 +02:00
+ if (!urgent) {
2021-06-16 12:14:53 +02:00
+ // If not urgent, at least use a slightly boosted priority
2021-06-11 14:02:28 +02:00
+ world.getChunkSource().markHighPriority(new ChunkPos(x, z), 1);
+ }
2021-06-16 12:14:53 +02:00
+ // Paper end
2021-06-11 14:02:28 +02:00
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);
2021-06-16 12:14:53 +02:00
if (chunk != null) addTicket(x, z); // Paper
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2021-07-12 21:42:17 +02:00
index 638dc61594e86a1423536d2a7fe8405d2451a4ee..ead9c15cfe31d1c9582826ad079691a1f147f3fa 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2021-07-12 21:42:17 +02:00
@@ -890,6 +890,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
2021-06-11 14:02:28 +02:00
throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
}
2021-06-16 12:14:53 +02:00
+ // Paper start - Chunk priority
2021-06-11 14:02:28 +02:00
+ @Override
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location loc, @javax.annotation.Nonnull PlayerTeleportEvent.TeleportCause cause) {
2021-06-16 12:14:53 +02:00
+ ((CraftWorld)loc.getWorld()).getHandle().getChunkSource().markAreaHighPriority(
+ new net.minecraft.world.level.ChunkPos(net.minecraft.util.Mth.floor(loc.getX()) >> 4,
+ net.minecraft.util.Mth.floor(loc.getZ()) >> 4), 28, 3); // Load area high priority
2021-06-11 14:02:28 +02:00
+ return super.teleportAsync(loc, cause);
+ }
+ // Paper end
2021-06-16 08:46:06 +02:00
+
2021-06-11 14:02:28 +02:00
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
Preconditions.checkArgument(location != null, "location");