2020-05-19 10:01:53 +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.
2020-05-20 11:11:57 +02:00
diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
2020-06-26 14:04:38 +02:00
index 49a594e5f345096cb3b9913b8ee2760795c5f012..bcb7ce2692c5bc1a6214c3a384949666107864fb 100644
2020-05-20 11:11:57 +02:00
--- a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java
@@ -4,7 +4,10 @@ import com.destroystokyo.paper.io.PaperFileIOThread;
import com.destroystokyo.paper.io.IOUtil;
import com.destroystokyo.paper.io.PrioritizedTaskQueue;
import com.destroystokyo.paper.io.QueueExecutorThread;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import net.minecraft.server.ChunkCoordIntPair;
import net.minecraft.server.ChunkRegionLoader;
+import net.minecraft.server.ChunkStatus;
import net.minecraft.server.IAsyncTaskHandler;
import net.minecraft.server.IChunkAccess;
import net.minecraft.server.MinecraftServer;
2020-05-23 01:03:48 +02:00
@@ -106,7 +109,7 @@ public final class ChunkTaskManager {
}
static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z) {
- dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 1);
+ dumpChunkInfo(seenChunks, chunkHolder, x, z, 0, 4);
}
static void dumpChunkInfo(Set<PlayerChunk> seenChunks, PlayerChunk chunkHolder, int x, int z, int indent, int maxDepth) {
@@ -127,6 +130,30 @@ public final class ChunkTaskManager {
2020-05-20 11:11:57 +02:00
PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getChunkStatus().toString()));
PaperFileIOThread.LOGGER.log(Level.ERROR, indentStr + "Chunk Ticket Status - " + PlayerChunk.getChunkStatus(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());
+
2020-05-23 01:03:48 +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 (PlayerChunk neighbor : chunkHolder.neighbors.keySet()) {
+ ChunkStatus status = neighbor.getChunkHolderStatus();
+ if (status != null && status.isAtLeastStatus(PlayerChunk.getChunkStatus(neighbor.getTicketLevel()))) {
+ continue;
2020-05-20 11:11:57 +02:00
+ }
2020-05-23 01:03:48 +02:00
+ int nx = neighbor.location.x;
+ int nz = neighbor.location.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);
2020-05-20 11:11:57 +02:00
+ }
+ }
2020-05-23 01:03:48 +02:00
+
2020-05-20 11:11:57 +02:00
}
}
2020-05-30 08:53:47 +02:00
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
2020-08-25 04:22:08 +02:00
index b12e43ed13a1a9d7d418831b8988c30a242cb5c4..dcaf9f8574a9c913b64ba3a1d8b02220db720225 100644
2020-05-30 08:53:47 +02:00
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
+++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
2020-08-25 04:22:08 +02:00
@@ -103,6 +103,7 @@ public class ChunkCoordIntPair {
2020-05-30 08:53:47 +02:00
return "[" + this.x + ", " + this.z + "]";
}
+ public final BlockPosition asPosition() { return l(); } // Paper - OBFHELPER
public BlockPosition l() {
2020-06-26 03:58:00 +02:00
return new BlockPosition(this.d(), 0, this.e());
2020-05-30 08:53:47 +02:00
}
2020-05-19 10:01:53 +02:00
diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
2020-08-25 04:22:08 +02:00
index 03b1a67aaf3ed75b7669a3157847affd678f8df4..09fec533d7feebb90e989007dd19c952823550ba 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
2020-05-22 06:46:44 +02:00
@@ -23,6 +23,7 @@ import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.spigotmc.AsyncCatcher; // Paper
public abstract class ChunkMapDistance {
2020-06-23 10:10:08 +02:00
@@ -46,7 +47,7 @@ public abstract class ChunkMapDistance {
private final ChunkTaskQueueSorter i;
private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> j;
private final Mailbox<ChunkTaskQueueSorter.b> k;
- private final LongSet l = new LongOpenHashSet();
2020-08-02 07:39:36 +02:00
+ private final LongSet l = new LongOpenHashSet(); public final LongSet getOnPlayerTicketAddQueue() { return l; } // Paper - OBFHELPER
2020-06-23 10:10:08 +02:00
private final Executor m;
private long currentTick;
2020-05-22 06:46:44 +02:00
@@ -84,6 +85,7 @@ public abstract class ChunkMapDistance {
}
2020-06-26 03:58:00 +02:00
private static int getLowestTicketLevel(ArraySetSorted<Ticket<?>> arraysetsorted) {
+ AsyncCatcher.catchOp("ChunkMapDistance::getLowestTicketLevel"); // Paper
2020-05-22 06:46:44 +02:00
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
}
2020-05-23 01:03:48 +02:00
@@ -97,6 +99,7 @@ public abstract class ChunkMapDistance {
public boolean a(PlayerChunkMap playerchunkmap) {
//this.f.a(); // Paper - no longer used
2020-06-26 03:58:00 +02:00
+ AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
2020-05-23 01:03:48 +02:00
this.g.a();
2020-06-26 03:58:00 +02:00
int i = Integer.MAX_VALUE - this.ticketLevelTracker.a(Integer.MAX_VALUE);
2020-05-23 01:03:48 +02:00
boolean flag = i != 0;
@@ -107,11 +110,13 @@ public abstract class ChunkMapDistance {
2020-05-22 06:46:44 +02:00
// Paper start
if (!this.pendingChunkUpdates.isEmpty()) {
2020-06-20 08:37:21 +02:00
+ this.pollingPendingChunkUpdates = true; try {
2020-05-22 06:46:44 +02:00
while(!this.pendingChunkUpdates.isEmpty()) {
PlayerChunk remove = this.pendingChunkUpdates.remove();
remove.isUpdateQueued = false;
remove.a(playerchunkmap);
}
2020-06-20 08:37:21 +02:00
+ } finally { this.pollingPendingChunkUpdates = false; }
2020-05-22 06:46:44 +02:00
// Paper end
return true;
} else {
2020-06-03 07:46:09 +02:00
@@ -147,8 +152,10 @@ public abstract class ChunkMapDistance {
2020-05-22 06:46:44 +02:00
return flag;
}
}
+ boolean pollingPendingChunkUpdates = false; // Paper
private boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+ AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper
ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
2020-06-26 03:58:00 +02:00
int j = getLowestTicketLevel(arraysetsorted);
2020-05-19 10:01:53 +02:00
Ticket<?> ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error
2020-06-08 23:03:42 +02:00
@@ -162,7 +169,9 @@ public abstract class ChunkMapDistance {
2020-05-22 06:46:44 +02:00
}
private boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+ AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
2020-06-26 03:58:00 +02:00
+ int oldLevel = getLowestTicketLevel(arraysetsorted); // Paper
2020-05-22 06:46:44 +02:00
boolean removed = false; // CraftBukkit
2020-06-08 23:03:42 +02:00
if (arraysetsorted.remove(ticket)) {
2020-06-26 03:58:00 +02:00
@@ -173,7 +182,8 @@ public abstract class ChunkMapDistance {
2020-06-08 23:03:42 +02:00
this.tickets.remove(i);
}
2020-06-26 03:58:00 +02:00
- this.ticketLevelTracker.update(i, getLowestTicketLevel(arraysetsorted), false);
+ int newLevel = getLowestTicketLevel(arraysetsorted); // Paper
+ if (newLevel > oldLevel) this.ticketLevelTracker.update(i, newLevel, false); // Paper
2020-06-08 23:03:42 +02:00
return removed; // CraftBukkit
}
2020-06-26 03:58:00 +02:00
@@ -182,6 +192,135 @@ public abstract class ChunkMapDistance {
2020-05-19 10:01:53 +02:00
this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0);
}
+ // Paper start
2020-06-08 23:03:42 +02:00
+ public static final int PRIORITY_TICKET_LEVEL = PlayerChunkMap.GOLDEN_TICKET;
2020-06-03 07:46:09 +02:00
+ public static final int URGENT_PRIORITY = 29;
2020-06-10 05:06:34 +02:00
+ public boolean delayDistanceManagerTick = false;
2020-05-19 10:01:53 +02:00
+ public boolean markUrgent(ChunkCoordIntPair coords) {
2020-06-03 07:46:09 +02:00
+ return addPriorityTicket(coords, TicketType.URGENT, URGENT_PRIORITY);
2020-05-19 10:01:53 +02:00
+ }
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
2020-06-03 07:46:09 +02:00
+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority));
2020-05-22 06:46:44 +02:00
+ return addPriorityTicket(coords, TicketType.PRIORITY, priority);
+ }
+
2020-06-10 05:06:34 +02:00
+ public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) {
+ delayDistanceManagerTick = true;
2020-06-23 10:10:08 +02:00
+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority));
+ int finalPriority = priority;
2020-06-10 05:06:34 +02:00
+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> {
2020-06-23 10:10:08 +02:00
+ addPriorityTicket(coords, TicketType.PRIORITY, finalPriority);
2020-06-10 05:06:34 +02:00
+ });
+ delayDistanceManagerTick = false;
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ }
+
+ public void clearAreaPriorityTickets(ChunkCoordIntPair center, int radius) {
+ delayDistanceManagerTick = true;
+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> {
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
+ });
+ delayDistanceManagerTick = false;
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ }
+
2020-06-23 10:10:08 +02:00
+ private boolean hasPlayerTicket(ChunkCoordIntPair coords, int level) {
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
+ if (tickets == null || tickets.isEmpty()) {
+ return false;
+ }
+ for (Ticket<?> ticket : tickets) {
+ if (ticket.getTicketType() == TicketType.PLAYER && ticket.getTicketLevel() == level) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
2020-05-22 06:46:44 +02:00
+ private boolean addPriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> ticketType, int priority) {
+ AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket");
2020-05-20 11:11:57 +02:00
+ long pair = coords.pair();
2020-06-10 05:06:34 +02:00
+ PlayerChunk chunk = chunkMap.getUpdatingChunk(pair);
2020-06-23 10:10:08 +02:00
+ boolean needsTicket = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null && !hasPlayerTicket(coords, 33);
+
+ if (needsTicket) {
2020-06-10 14:06:34 +02:00
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords);
2020-06-23 10:10:08 +02:00
+ getOnPlayerTicketAddQueue().add(pair);
2020-06-10 14:06:34 +02:00
+ addTicket(pair, ticket);
+ }
2020-06-23 10:10:08 +02:00
+ if ((chunk != null && chunk.isFullChunkReady())) {
+ if (needsTicket) {
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ }
+ return needsTicket;
+ }
2020-06-09 09:17:25 +02:00
+
2020-05-23 01:03:48 +02:00
+ boolean success;
+ if (!(success = updatePriorityTicket(coords, ticketType, priority))) {
+ Ticket<ChunkCoordIntPair> ticket = new Ticket<ChunkCoordIntPair>(ticketType, PRIORITY_TICKET_LEVEL, coords);
+ ticket.priority = priority;
+ success = this.addTicket(pair, ticket);
2020-06-09 09:17:25 +02:00
+ } else {
2020-06-10 05:06:34 +02:00
+ if (chunk == null) {
+ chunk = chunkMap.getUpdatingChunk(pair);
2020-06-09 09:17:25 +02:00
+ }
2020-06-10 05:06:34 +02:00
+ chunkMap.queueHolderUpdate(chunk);
2020-05-23 01:03:48 +02:00
+ }
2020-06-10 05:06:34 +02:00
+
+ //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);
+
2020-06-09 09:17:25 +02:00
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+
2020-05-23 01:03:48 +02:00
+ return success;
+ }
+
+ private boolean updatePriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> type, int priority) {
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
+ if (tickets == null) {
+ return false;
+ }
+ for (Ticket<?> ticket : tickets) {
+ if (ticket.getTicketType() == type) {
+ // We only support increasing, not decreasing, too complicated
2020-06-09 09:17:25 +02:00
+ ticket.setCurrentTick(this.currentTick);
2020-05-23 01:03:48 +02:00
+ ticket.priority = Math.max(ticket.priority, priority);
+ return true;
+ }
2020-05-22 06:46:44 +02:00
+ }
2020-05-23 01:03:48 +02:00
+
+ return false;
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+
2020-05-19 10:01:53 +02:00
+ public int getChunkPriority(ChunkCoordIntPair coords) {
2020-05-22 06:46:44 +02:00
+ AsyncCatcher.catchOp("ChunkMapDistance::getChunkPriority");
2020-05-19 10:01:53 +02:00
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
+ if (tickets == null) {
2020-05-22 06:46:44 +02:00
+ return 0;
2020-05-19 10:01:53 +02:00
+ }
+ for (Ticket<?> ticket : tickets) {
2020-05-22 06:46:44 +02:00
+ if (ticket.getTicketType() == TicketType.URGENT) {
2020-06-03 07:46:09 +02:00
+ return URGENT_PRIORITY;
2020-05-19 10:01:53 +02:00
+ }
+ }
+ for (Ticket<?> ticket : tickets) {
2020-05-22 06:46:44 +02:00
+ if (ticket.getTicketType() == TicketType.PRIORITY && ticket.priority > 0) {
+ return ticket.priority;
2020-05-19 10:01:53 +02:00
+ }
+ }
2020-05-22 06:46:44 +02:00
+ return 0;
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+
2020-05-20 11:11:57 +02:00
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
2020-05-22 06:46:44 +02:00
+ AsyncCatcher.catchOp("ChunkMapDistance::clearPriority");
2020-05-23 01:03:48 +02:00
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
2020-05-22 06:46:44 +02:00
+ }
+
+ public void clearUrgent(ChunkCoordIntPair coords) {
+ AsyncCatcher.catchOp("ChunkMapDistance::clearUrgent");
2020-05-23 01:03:48 +02:00
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.URGENT, PRIORITY_TICKET_LEVEL, coords));
2020-05-20 11:11:57 +02:00
+ }
2020-05-19 10:01:53 +02:00
+ // Paper end
public <T> boolean addTicketAtLevel(TicketType<T> ticketType, ChunkCoordIntPair chunkcoordintpair, int level, T identifier) {
return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier));
// CraftBukkit end
2020-06-26 03:58:00 +02:00
@@ -381,27 +520,50 @@ public abstract class ChunkMapDistance {
2020-06-23 10:10:08 +02:00
private void a(long i, int j, boolean flag, boolean flag1) {
if (flag != flag1) {
- Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkCoordIntPair(i)); // Paper - no-tick view distance
+ ChunkCoordIntPair coords = new ChunkCoordIntPair(i); // Paper
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords); // Paper - no-tick view distance
2020-05-30 08:53:47 +02:00
if (flag1) {
2020-06-26 03:58:00 +02:00
- ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> {
2020-06-23 10:10:08 +02:00
+ scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> { // Paper - smarter ticket delay based on frustum and distance
+ // Paper start - recheck its still valid if not cancel
+ if (!isChunkInRange(i)) {
+ ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
+ ChunkMapDistance.this.m.execute(() -> {
+ ChunkMapDistance.this.removeTicket(i, ticket);
+ ChunkMapDistance.this.clearPriorityTickets(coords);
+ });
+ }, i, 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) {
+ ChunkMapDistance.this.addTicket(i, ticket);
+ ChunkMapDistance.this.l.add(i);
+ return;
+ }
+ // Paper end
2020-06-26 03:58:00 +02:00
+ ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
2020-06-23 10:10:08 +02:00
ChunkMapDistance.this.m.execute(() -> {
2020-06-09 09:17:25 +02:00
- if (this.c(this.c(i))) {
2020-06-23 10:10:08 +02:00
+ if (isChunkInRange(i)) { if (!hasPlayerTicket(coords, 33)) { // Paper - high priority might of already added it
2020-05-30 08:53:47 +02:00
ChunkMapDistance.this.addTicket(i, ticket);
ChunkMapDistance.this.l.add(i);
2020-06-23 10:10:08 +02:00
- } else {
2020-06-26 03:58:00 +02:00
- ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
+ }} else { // Paper
+ ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
2020-06-09 09:17:25 +02:00
}, i, false));
}
2020-06-23 10:10:08 +02:00
});
}, i, () -> {
2020-05-19 10:01:53 +02:00
- return j;
2020-06-23 10:10:08 +02:00
+ return Math.min(PlayerChunkMap.GOLDEN_TICKET, priority); // Paper
}));
2020-06-20 08:37:21 +02:00
+ }); // Paper
2020-05-19 10:01:53 +02:00
} else {
2020-06-26 03:58:00 +02:00
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
2020-05-22 06:46:44 +02:00
ChunkMapDistance.this.m.execute(() -> {
ChunkMapDistance.this.removeTicket(i, ticket);
2020-06-23 10:10:08 +02:00
+ ChunkMapDistance.this.clearPriorityTickets(coords); // Paper
2020-05-22 06:46:44 +02:00
});
}, i, true));
}
2020-08-02 07:39:36 +02:00
@@ -409,6 +571,101 @@ public abstract class ChunkMapDistance {
2020-06-09 09:17:25 +02:00
}
+ // Paper start - smart scheduling of player tickets
2020-06-23 10:10:08 +02:00
+ private boolean isChunkInRange(long i) {
+ return this.isLoadedChunkLevel(this.getChunkLevel(i));
+ }
2020-06-10 05:06:34 +02:00
+ public void scheduleChunkLoad(long i, long startTick, int initialDistance, java.util.function.Consumer<Integer> task) {
2020-06-09 09:17:25 +02:00
+ long elapsed = MinecraftServer.currentTick - startTick;
2020-06-10 14:06:34 +02:00
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i);
2020-06-09 09:17:25 +02:00
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(i);
2020-06-23 10:10:08 +02:00
+ if ((updatingChunk != null && updatingChunk.isFullChunkReady()) || !isChunkInRange(i) || getChunkPriority(chunkPos) > 0) { // Copied from above
2020-06-09 09:17:25 +02:00
+ // no longer needed
2020-06-10 14:06:34 +02:00
+ task.accept(1);
2020-06-09 09:17:25 +02:00
+ return;
+ }
+
+ int desireDelay = 0;
+ double minDist = Double.MAX_VALUE;
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i);
2020-06-10 05:06:34 +02:00
+ 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) {
2020-06-09 09:17:25 +02:00
+ Object[] backingSet = players.getBackingSet();
+
+ BlockPosition blockPos = chunkPos.asPosition();
+
+ boolean isFront = false;
2020-08-02 07:39:36 +02:00
+ BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition();
2020-06-09 09:17:25 +02:00
+ for (int index = 0, len = backingSet.length; index < len; ++index) {
+ if (!(backingSet[index] instanceof EntityPlayer)) {
+ continue;
+ }
+ EntityPlayer player = (EntityPlayer) backingSet[index];
2020-06-10 05:06:34 +02:00
+
+ ChunkCoordIntPair pointInFront = player.getChunkInFront(5);
+ pos.setValues(pointInFront.x << 4, 0, pointInFront.z << 4);
+ double frontDist = MCUtil.distanceSq(pos, blockPos);
+
+ pos.setValues(player.locX(), 0, player.locZ());
2020-06-09 09:17:25 +02:00
+ double center = MCUtil.distanceSq(pos, blockPos);
2020-06-10 05:06:34 +02:00
+
2020-06-09 09:17:25 +02:00
+ double dist = Math.min(frontDist, center);
+ if (!isFront) {
2020-06-23 10:10:08 +02:00
+ ChunkCoordIntPair pointInBack = player.getChunkInFront(-7);
2020-06-10 05:06:34 +02:00
+ pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4);
+ double backDist = MCUtil.distanceSq(pos, blockPos);
2020-06-09 09:17:25 +02:00
+ if (frontDist < backDist) {
+ isFront = true;
+ }
+ }
+ if (dist < minDist) {
+ minDist = dist;
+ }
+ }
2020-06-10 05:06:34 +02:00
+ if (minDist == Double.MAX_VALUE) {
+ minDist = 15;
+ } else {
2020-06-09 09:17:25 +02:00
+ minDist = Math.sqrt(minDist) / 16;
2020-06-10 05:06:34 +02:00
+ }
+ if (minDist > 4) {
+ int desiredTimeDelayMax = isFront ?
2020-06-23 10:10:08 +02:00
+ (minDist < 10 ? 7 : 15) : // Front
+ (minDist < 10 ? 15 : 45); // Back
2020-06-10 05:06:34 +02:00
+ desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32);
2020-06-09 09:17:25 +02:00
+ }
+ } else {
2020-06-10 05:06:34 +02:00
+ minDist = initialDistance;
2020-06-09 09:17:25 +02:00
+ 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;
2020-06-23 10:10:08 +02:00
+ long pair = ChunkCoordIntPair.pair(chunkPos.x + x, chunkPos.z + z);
2020-06-09 09:17:25 +02:00
+ PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair);
2020-06-10 05:06:34 +02:00
+ ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null;
+ if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) {
2020-06-09 09:17:25 +02:00
+ hasAnyNeighbor = true;
+ }
+ }
+ }
+ if (!hasAnyNeighbor) {
2020-06-23 10:10:08 +02:00
+ delay += 20;
2020-06-09 09:17:25 +02:00
+ }
+ }
+ if (delay <= 0) {
2020-06-10 05:06:34 +02:00
+ task.accept((int) minDist);
2020-06-09 09:17:25 +02:00
+ } else {
2020-06-23 10:10:08 +02:00
+ int taskDelay = (int) Math.min(delay, minDist >= 10 ? 40 : (minDist < 6 ? 5 : 20));
+ MCUtil.scheduleTask(taskDelay, () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer");
2020-06-09 09:17:25 +02:00
+ }
+ }
+ // Paper end
+
@Override
public void a() {
super.a();
2020-08-02 07:39:36 +02:00
@@ -440,6 +697,7 @@ public abstract class ChunkMapDistance {
2020-06-23 10:10:08 +02:00
}
+ private boolean isLoadedChunkLevel(int i) { return c(i); } // Paper - OBFHELPER
private boolean c(int i) {
return i <= this.e - 2;
}
2020-08-02 07:39:36 +02:00
@@ -456,6 +714,7 @@ public abstract class ChunkMapDistance {
2020-06-23 10:10:08 +02:00
this.a.defaultReturnValue((byte) (i + 2));
}
+ protected final int getChunkLevel(long i) { return c(i); } // Paper - OBFHELPER
@Override
protected int c(long i) {
return this.a.get(i);
2020-05-19 10:01:53 +02:00
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
2020-11-03 03:22:15 +01:00
index b4d8657d37b2a6c02e886ec6de243634d1c08d51..4b45a2bf926c26eddc7d81c233ab61870794ecdc 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
2020-08-25 04:22:08 +02:00
@@ -444,6 +444,26 @@ public class ChunkProviderServer extends IChunkProvider {
2020-05-19 10:01:53 +02:00
public <T> void removeTicketAtLevel(TicketType<T> ticketType, ChunkCoordIntPair chunkPos, int ticketLevel, T identifier) {
this.chunkMapDistance.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier);
}
+
+ public boolean markUrgent(ChunkCoordIntPair coords) {
2020-05-22 06:46:44 +02:00
+ return this.chunkMapDistance.markUrgent(coords);
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+
2020-05-19 10:01:53 +02:00
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
2020-05-22 06:46:44 +02:00
+ return this.chunkMapDistance.markHighPriority(coords, priority);
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+
2020-06-10 05:06:34 +02:00
+ public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) {
+ this.chunkMapDistance.markAreaHighPriority(center, priority, radius);
+ }
+
+ public void clearAreaPriorityTickets(ChunkCoordIntPair center, int radius) {
+ this.chunkMapDistance.clearAreaPriorityTickets(center, radius);
+ }
+
2020-05-19 10:01:53 +02:00
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
+ this.chunkMapDistance.clearPriorityTickets(coords);
+ }
// Paper end
@Nullable
2020-08-25 04:22:08 +02:00
@@ -482,6 +502,8 @@ public class ChunkProviderServer extends IChunkProvider {
2020-05-19 10:01:53 +02:00
if (!completablefuture.isDone()) { // Paper
// Paper start - async chunk io/loading
+ ChunkCoordIntPair pair = new ChunkCoordIntPair(x, z);
2020-05-22 06:46:44 +02:00
+ this.chunkMapDistance.markUrgent(pair);
2020-05-19 10:01:53 +02:00
this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z);
// Paper end
2020-08-25 04:22:08 +02:00
@@ -490,6 +512,8 @@ public class ChunkProviderServer extends IChunkProvider {
2020-05-22 06:46:44 +02:00
this.serverThreadQueue.awaitTasks(completablefuture::isDone);
2020-05-19 10:01:53 +02:00
com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug
this.world.timings.syncChunkLoad.stopTiming(); // Paper
2020-05-22 06:46:44 +02:00
+ this.chunkMapDistance.clearPriorityTickets(pair); // Paper
+ this.chunkMapDistance.clearUrgent(pair); // Paper
2020-05-19 10:01:53 +02:00
} // Paper
ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
return ichunkaccess1;
2020-08-25 04:22:08 +02:00
@@ -542,10 +566,12 @@ public class ChunkProviderServer extends IChunkProvider {
2020-06-20 20:23:09 +02:00
if (flag && !currentlyUnloading) {
2020-05-19 10:01:53 +02:00
// CraftBukkit end
this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
2020-05-22 06:46:44 +02:00
+ if (isUrgent) this.chunkMapDistance.markUrgent(chunkcoordintpair); // Paper
2020-05-19 10:01:53 +02:00
if (this.a(playerchunk, l)) {
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
2020-06-20 08:37:21 +02:00
gameprofilerfiller.enter("chunkLoad");
+ chunkMapDistance.delayDistanceManagerTick = false; // Paper - ensure this is never false
this.tickDistanceManager();
playerchunk = this.getChunk(k);
gameprofilerfiller.exit();
2020-08-25 04:22:08 +02:00
@@ -554,8 +580,13 @@ public class ChunkProviderServer extends IChunkProvider {
2020-05-19 10:01:53 +02:00
}
}
}
-
- return this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap);
+ // Paper start
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap);
+ if (isUrgent) {
2020-05-22 06:46:44 +02:00
+ future.thenAccept(either -> this.chunkMapDistance.clearUrgent(chunkcoordintpair));
2020-05-19 10:01:53 +02:00
+ }
+ return future;
+ // Paper end
}
private boolean a(@Nullable PlayerChunk playerchunk, int i) {
2020-08-25 04:22:08 +02:00
@@ -606,6 +637,7 @@ public class ChunkProviderServer extends IChunkProvider {
2020-05-22 06:46:44 +02:00
}
2020-06-26 03:58:00 +02:00
public boolean tickDistanceManager() { // Paper - private -> public
2020-06-10 05:06:34 +02:00
+ if (chunkMapDistance.delayDistanceManagerTick) return false; // Paper
2020-05-22 06:46:44 +02:00
boolean flag = this.chunkMapDistance.a(this.playerChunkMap);
boolean flag1 = this.playerChunkMap.b();
2020-05-19 10:01:53 +02:00
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
2020-11-18 04:45:18 +01:00
index 59554edff841a09e74465ff1101d09d049ea9b7e..66920fa15720976a84a33ce498b8ca6f5ad5dee3 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
2020-08-25 04:22:08 +02:00
@@ -56,6 +56,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
2020-06-10 05:06:34 +02:00
private int lastArmorScored = Integer.MIN_VALUE;
private int lastExpLevelScored = Integer.MIN_VALUE;
private int lastExpTotalScored = Integer.MIN_VALUE;
+ public long lastHighPriorityChecked; // Paper
2020-06-23 10:10:08 +02:00
+ public void forceCheckHighPriority() {
+ lastHighPriorityChecked = -1;
+ getWorldServer().getChunkProvider().playerChunkMap.checkHighPriorityChunks(this);
+ }
+ public boolean isRealPlayer; // Paper
2020-06-10 05:06:34 +02:00
private float lastHealthSent = -1.0E8F;
private int lastFoodSent = -99999999;
private boolean lastSentSaturationZero = true;
2020-11-03 03:22:15 +01:00
@@ -141,6 +147,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
2020-05-30 08:53:47 +02:00
this.maxHealthCache = this.getMaxHealth();
2020-06-27 06:57:36 +02:00
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
2020-05-30 08:53:47 +02:00
}
+ // Paper start
+ public BlockPosition getPointInFront(double inFront) {
2020-06-10 05:06:34 +02:00
+ double rads = Math.toRadians(MCUtil.normalizeYaw(this.yaw+90)); // MC rotates yaw 90 for some odd reason
2020-05-30 08:53:47 +02:00
+ final double x = locX() + inFront * Math.cos(rads);
+ final double z = locZ() + inFront * Math.sin(rads);
+ return new BlockPosition(x, locY(), z);
+ }
2020-06-10 05:06:34 +02:00
+
+ public ChunkCoordIntPair getChunkInFront(double inFront) {
+ double rads = Math.toRadians(MCUtil.normalizeYaw(this.yaw+90)); // MC rotates yaw 90 for some odd reason
+ final double x = locX() + (inFront * 16) * Math.cos(rads);
+ final double z = locZ() + (inFront * 16) * Math.sin(rads);
+ return new ChunkCoordIntPair(MathHelper.floor(x) >> 4, MathHelper.floor(z) >> 4);
+ }
2020-05-30 08:53:47 +02:00
+ // 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
2020-11-03 03:22:15 +01:00
@@ -488,6 +509,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
2020-06-26 03:58:00 +02:00
if (valid && !this.isSpectator() || this.world.isLoaded(this.getChunkCoordinates())) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
2020-05-19 10:01:53 +02:00
super.tick();
}
2020-06-23 10:10:08 +02:00
+ if (valid && isAlive() && playerConnection != null) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper
2020-05-19 10:01:53 +02:00
for (int i = 0; i < this.inventory.getSize(); ++i) {
ItemStack itemstack = this.inventory.getItem(i);
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
2020-08-25 04:22:08 +02:00
index 0b5ddff008d151ad03a1f382a8f24494356e8701..ff74be14512a947e81b62d53e616131ca7d7f609 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
2020-08-25 04:22:08 +02:00
@@ -656,6 +656,7 @@ public final class MCUtil {
2020-05-19 10:01:53 +02:00
chunkData.addProperty("x", playerChunk.location.x);
chunkData.addProperty("z", playerChunk.location.z);
chunkData.addProperty("ticket-level", playerChunk.getTicketLevel());
+ chunkData.addProperty("priority", playerChunk.getCurrentPriority());
chunkData.addProperty("state", PlayerChunk.getChunkState(playerChunk.getTicketLevel()).toString());
chunkData.addProperty("queued-for-unload", chunkMap.unloadQueue.contains(playerChunk.location.pair()));
chunkData.addProperty("status", status == null ? "unloaded" : status.toString());
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
2020-10-08 07:07:10 +02:00
index 078cbc9f4dbd387ef1088c76b2f77ee47fff135d..ca9e0208aa35c597a947485f67213ff4b1b1185f 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
2020-08-25 04:22:08 +02:00
@@ -1,6 +1,7 @@
package net.minecraft.server;
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;
@@ -28,8 +29,8 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
private CompletableFuture<IChunkAccess> chunkSave;
public int oldTicketLevel;
private int ticketLevel;
- private int n;
2020-05-20 11:11:57 +02:00
- final ChunkCoordIntPair location; // Paper - private -> package
+ volatile int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER - make volatile since this is concurrently accessed
+ public final ChunkCoordIntPair location; // Paper - private -> public
2020-08-25 04:22:08 +02:00
private boolean p;
private final ShortSet[] dirtyBlocks;
2020-05-20 11:11:57 +02:00
private int r;
2020-08-25 04:22:08 +02:00
@@ -41,6 +42,7 @@ public class PlayerChunk {
private boolean x;
2020-05-20 11:11:57 +02:00
private final PlayerChunkMap chunkMap; // Paper
+ public WorldServer getWorld() { return chunkMap.world; } // Paper
long lastAutoSaveTime; // Paper - incremental autosave
long inactiveTimeStart; // Paper - incremental autosave
2020-08-25 04:22:08 +02:00
@@ -68,6 +70,120 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
return null;
}
// Paper end - no-tick view distance
+ // Paper start - Chunk gen/load priority system
+ volatile int neighborPriority = -1;
2020-05-22 06:46:44 +02:00
+ volatile int priorityBoost = 0;
2020-05-20 11:11:57 +02:00
+ public final java.util.concurrent.ConcurrentHashMap<PlayerChunk, ChunkStatus> neighbors = new java.util.concurrent.ConcurrentHashMap<>();
2020-08-25 04:22:08 +02:00
+ public final Long2ObjectOpenHashMap<Integer> neighborPriorities = new Long2ObjectOpenHashMap<>();
2020-05-19 10:01:53 +02:00
+
2020-05-22 06:46:44 +02:00
+ private int getDemandedPriority() {
2020-05-19 10:01:53 +02:00
+ int priority = neighborPriority; // if we have a neighbor priority, use it
2020-05-22 06:46:44 +02:00
+ int myPriority = getMyPriority();
2020-05-19 10:01:53 +02:00
+
2020-05-23 01:03:48 +02:00
+ if (priority == -1 || (ticketLevel <= 33 && priority > myPriority)) {
2020-05-22 06:46:44 +02:00
+ priority = myPriority;
2020-05-19 10:01:53 +02:00
+ }
+
2020-06-08 23:03:42 +02:00
+ return Math.max(1, Math.min(Math.max(ticketLevel, PlayerChunkMap.GOLDEN_TICKET), priority));
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+
+ private int getMyPriority() {
2020-06-03 07:46:09 +02:00
+ if (priorityBoost == ChunkMapDistance.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
2020-05-22 06:46:44 +02:00
+ }
2020-06-20 08:37:21 +02:00
+ return ticketLevel - priorityBoost;
2020-05-19 10:01:53 +02:00
+ }
+
2020-05-22 06:46:44 +02:00
+ private int getNeighborsPriority() {
2020-10-08 07:07:10 +02:00
+ return (neighborPriorities.isEmpty() ? getMyPriority() : getDemandedPriority()) + 1;
2020-05-22 06:46:44 +02:00
+ }
+
+ public void onNeighborRequest(PlayerChunk 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;
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+ });
+
2020-05-19 10:01:53 +02:00
+ }
+
2020-05-22 06:46:44 +02:00
+ public void onNeighborDone(PlayerChunk neighbor, ChunkStatus chunkstatus, IChunkAccess 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;
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+ });
+ }
+
+ private void removeNeighborPriority(PlayerChunk requester) {
+ synchronized (neighborPriorities) {
+ neighborPriorities.remove(requester.location.pair());
+ recalcNeighborPriority();
2020-05-19 10:01:53 +02:00
+ }
2020-05-22 06:46:44 +02:00
+ checkPriority();
+ }
+
+
+ private void setNeighborPriority(PlayerChunk requester, int priority) {
+ synchronized (neighborPriorities) {
+ neighborPriorities.put(requester.location.pair(), Integer.valueOf(priority));
+ recalcNeighborPriority();
+ }
+ checkPriority();
2020-05-19 10:01:53 +02:00
+ }
+
+ private void recalcNeighborPriority() {
+ neighborPriority = -1;
+ if (!neighborPriorities.isEmpty()) {
+ synchronized (neighborPriorities) {
+ for (Integer neighbor : neighborPriorities.values()) {
+ if (neighbor < neighborPriority || neighborPriority == -1) {
+ neighborPriority = neighbor;
+ }
+ }
+ }
+ }
+ }
2020-05-22 06:46:44 +02:00
+ private void checkPriority() {
+ if (getCurrentPriority() != getDemandedPriority()) this.chunkMap.queueHolderUpdate(this);
+ }
2020-05-19 10:01:53 +02:00
+
+ public final double getDistance(EntityPlayer player) {
+ return getDistance(player.locX(), player.locZ());
+ }
+ public final double getDistance(double blockX, double blockZ) {
+ int cx = MCUtil.fastFloor(blockX) >> 4;
+ int cz = MCUtil.fastFloor(blockZ) >> 4;
+ final double x = location.x - cx;
+ final double z = location.z - cz;
+ return (x * x) + (z * z);
+ }
2020-05-30 08:53:47 +02:00
+
+ public final double getDistanceFrom(BlockPosition pos) {
+ return getDistance(pos.getX(), pos.getZ());
+ }
+
2020-05-22 06:46:44 +02:00
+ @Override
+ public String toString() {
+ return "PlayerChunk{" +
+ "location=" + location +
+ ", ticketLevel=" + ticketLevel + "/" + getChunkStatus(this.ticketLevel) +
+ ", chunkHolderStatus=" + getChunkHolderStatus() +
+ ", neighborPriority=" + getNeighborsPriority() +
+ ", priority=(" + ticketLevel + " - " + priorityBoost +" vs N " + neighborPriority + ") = " + getDemandedPriority() + " A " + getCurrentPriority() +
+ '}';
+ }
2020-05-19 10:01:53 +02:00
+ // Paper end
public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) {
this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size());
2020-08-25 04:22:08 +02:00
@@ -166,6 +282,18 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
}
return null;
}
+ public static ChunkStatus getNextStatus(ChunkStatus status) {
+ if (status == ChunkStatus.FULL) {
+ return status;
+ }
+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1);
2020-05-23 01:03:48 +02:00
+ }
+ public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) {
2020-06-23 10:10:08 +02:00
+ return ensureMain(getStatusFutureUnchecked(chunkstatus));
+ }
+ public <T> CompletableFuture<T> ensureMain(CompletableFuture<T> future) {
+ return future.thenApplyAsync(r -> r, chunkMap.mainInvokingExecutor);
2020-05-19 10:01:53 +02:00
+ }
// Paper end
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
2020-08-25 04:22:08 +02:00
@@ -412,6 +540,7 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
return this.n;
}
+ private void setPriority(int i) { d(i); } // Paper - OBFHELPER
private void d(int i) {
this.n = i;
}
2020-08-25 04:22:08 +02:00
@@ -430,7 +559,7 @@ public class PlayerChunk {
2020-05-23 01:03:48 +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.isAtLeast(PlayerChunk.State.BORDER) && !playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
- this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
Chunk chunk = (Chunk)either.left().orElse(null);
if (chunk != null) {
playerchunkmap.callbackExecutor.execute(() -> {
2020-08-25 04:22:08 +02:00
@@ -495,12 +624,13 @@ public class PlayerChunk {
2020-05-23 01:03:48 +02:00
if (!flag2 && flag3) {
// Paper start - cache ticking ready status
int expectCreateCount = ++this.fullChunkCreateCount;
- this.fullChunkFuture = playerchunkmap.b(this); this.fullChunkFuture.thenAccept((either) -> {
2020-06-23 10:10:08 +02:00
+ this.fullChunkFuture = playerchunkmap.b(this); ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main
2020-05-23 01:03:48 +02:00
if (either.left().isPresent() && PlayerChunk.this.fullChunkCreateCount == expectCreateCount) {
// note: Here is a very good place to add callbacks to logic waiting on this.
2020-05-19 10:01:53 +02:00
Chunk fullChunk = either.left().get();
PlayerChunk.this.isFullChunkReady = true;
fullChunk.playerChunk = PlayerChunk.this;
+ this.chunkMap.chunkDistanceManager.clearPriorityTickets(location);
}
2020-08-25 04:22:08 +02:00
@@ -525,7 +655,7 @@ public class PlayerChunk {
2020-05-23 01:03:48 +02:00
if (!flag4 && flag5) {
// Paper start - cache ticking ready status
- this.tickingFuture = playerchunkmap.a(this); this.tickingFuture.thenAccept((either) -> {
2020-06-23 10:10:08 +02:00
+ this.tickingFuture = playerchunkmap.a(this); ensureMain(this.tickingFuture).thenAccept((either) -> { // Paper - ensure main
2020-05-23 01:03:48 +02:00
if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this.
Chunk tickingChunk = either.left().get();
2020-08-25 04:22:08 +02:00
@@ -556,7 +686,7 @@ public class PlayerChunk {
2020-05-23 01:03:48 +02:00
}
// Paper start - cache ticking ready status
- this.entityTickingFuture = playerchunkmap.b(this.location); this.entityTickingFuture.thenAccept((either) -> {
2020-06-23 10:10:08 +02:00
+ this.entityTickingFuture = playerchunkmap.b(this.location); ensureMain(this.entityTickingFuture).thenAccept((either) -> { // Paper ensureMain
2020-05-23 01:03:48 +02:00
if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this.
Chunk entityTickingChunk = either.left().get();
2020-08-25 04:22:08 +02:00
@@ -576,12 +706,29 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
}
2020-06-26 03:58:00 +02:00
2020-08-25 04:22:08 +02:00
- this.u.a(this.location, this::k, this.ticketLevel, this::d);
2020-05-20 11:11:57 +02:00
+ // Paper start - raise IO/load priority if priority changes, use our preferred priority
2020-05-22 06:46:44 +02:00
+ priorityBoost = chunkMap.chunkDistanceManager.getChunkPriority(location);
+ int priority = getDemandedPriority();
2020-05-20 11:11:57 +02:00
+ 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.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority);
+ }
2020-05-23 01:03:48 +02:00
+ if (getCurrentPriority() != priority) {
2020-08-25 04:22:08 +02:00
+ this.u.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
2020-05-23 01:03:48 +02:00
+ int neighborsPriority = getNeighborsPriority();
+ this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority));
+ }
2020-05-20 11:11:57 +02:00
+ // Paper end
2020-05-19 10:01:53 +02:00
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.
2020-05-23 01:03:48 +02:00
if (!playerchunk_state.isAtLeast(PlayerChunk.State.BORDER) && playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
- this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
Chunk chunk = (Chunk)either.left().orElse(null);
if (chunk != null) {
playerchunkmap.callbackExecutor.execute(() -> {
2020-08-25 04:22:08 +02:00
@@ -663,6 +810,7 @@ public class PlayerChunk {
2020-05-19 10:01:53 +02:00
public interface c {
+ default void changePriority(ChunkCoordIntPair chunkcoordintpair, IntSupplier intsupplier, int i, IntConsumer intconsumer) { a(chunkcoordintpair, intsupplier, i, intconsumer); } // Paper - OBFHELPER
void a(ChunkCoordIntPair chunkcoordintpair, IntSupplier intsupplier, int i, IntConsumer intconsumer);
}
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
2020-10-12 00:24:06 +02:00
index fba7c9b5724114eab35a3d24febdee3cd3e30aa0..b6ae589548bdc75052f4986069b3374cb5fd837c 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
2020-08-25 04:22:08 +02:00
@@ -14,6 +14,7 @@ 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;
@@ -52,6 +53,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
2020-05-23 01:03:48 +02:00
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.entity.Player; // CraftBukkit
+import org.spigotmc.AsyncCatcher;
public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-08-25 04:22:08 +02:00
@@ -89,6 +91,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-06-26 03:58:00 +02:00
public final WorldServer world;
private final LightEngineThreaded lightEngine;
private final IAsyncTaskHandler<Runnable> executor;
+ final java.util.concurrent.Executor mainInvokingExecutor; // Paper
public final ChunkGenerator chunkGenerator;
private final Supplier<WorldPersistentData> l; public final Supplier<WorldPersistentData> getWorldPersistentDataSupplier() { return this.l; } // Paper - OBFHELPER
private final VillagePlace m;
2020-08-25 04:22:08 +02:00
@@ -126,6 +129,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-23 01:03:48 +02:00
@Override
public void execute(Runnable runnable) {
+ AsyncCatcher.catchOp("Callback Executor execute");
if (queued == null) {
queued = new java.util.ArrayDeque<>();
}
2020-08-25 04:22:08 +02:00
@@ -134,6 +138,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-23 01:03:48 +02:00
@Override
public void run() {
+ AsyncCatcher.catchOp("Callback Executor run");
if (queued == null) {
return;
}
2020-08-25 04:22:08 +02:00
@@ -288,6 +293,15 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-06-26 03:58:00 +02:00
this.world = worldserver;
this.chunkGenerator = chunkgenerator;
this.executor = iasynctaskhandler;
+ // Paper start
+ this.mainInvokingExecutor = (run) -> {
+ if (MCUtil.isMainThread()) {
+ run.run();
+ } else {
+ iasynctaskhandler.execute(run);
+ }
+ };
+ // Paper end
ThreadedMailbox<Runnable> threadedmailbox = ThreadedMailbox.a(executor, "worldgen");
iasynctaskhandler.getClass();
2020-08-25 04:22:08 +02:00
@@ -382,6 +396,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
(EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
+ checkHighPriorityChunks(player);
if (newState.size() != 1) {
return;
}
2020-08-25 04:22:08 +02:00
@@ -400,7 +415,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
}
ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(rangeX, rangeZ);
PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
- });
+ PlayerChunkMap.this.world.getChunkProvider().clearPriorityTickets(chunkPos);
2020-06-23 10:10:08 +02:00
+ }, (player, prevPos, newPos) -> {
+ player.lastHighPriorityChecked = -1; // reset and recheck
+ checkHighPriorityChunks(player);
+ });
2020-05-19 10:01:53 +02:00
this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
(EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
2020-08-25 04:22:08 +02:00
@@ -417,6 +436,115 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
});
// Paper end - no-tick view distance
}
+ // Paper start - Chunk Prioritization
2020-05-22 06:46:44 +02:00
+ public void queueHolderUpdate(PlayerChunk playerchunk) {
2020-05-23 01:03:48 +02:00
+ Runnable runnable = () -> {
+ if (isUnloading(playerchunk)) {
+ return; // unloaded
+ }
2020-05-22 06:46:44 +02:00
+ chunkDistanceManager.pendingChunkUpdates.add(playerchunk);
2020-06-09 09:17:25 +02:00
+ if (!chunkDistanceManager.pollingPendingChunkUpdates) {
+ world.getChunkProvider().tickDistanceManager();
+ }
2020-05-23 01:03:48 +02:00
+ };
+ 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 {
+ executor.execute(runnable);
+ }
2020-05-22 06:46:44 +02:00
+ }
+
2020-06-09 09:17:25 +02:00
+ private boolean isUnloading(PlayerChunk playerchunk) {
+ return playerchunk == null || unloadQueue.contains(playerchunk.location.pair());
2020-05-22 06:46:44 +02:00
+ }
+
2020-08-25 04:22:08 +02:00
+ private void updateChunkPriorityMap(Long2IntOpenHashMap map, long chunk, int level) {
2020-06-23 10:10:08 +02:00
+ int prev = map.getOrDefault(chunk, -1);
+ if (level > prev) {
+ map.put(chunk, level);
+ }
+ }
+
2020-05-19 10:01:53 +02:00
+ public void checkHighPriorityChunks(EntityPlayer player) {
2020-06-10 05:06:34 +02:00
+ int currentTick = MinecraftServer.currentTick;
2020-06-23 10:10:08 +02:00
+ if (currentTick - player.lastHighPriorityChecked < 20 || !player.isRealPlayer) { // weed out fake players
2020-06-10 05:06:34 +02:00
+ return;
+ }
+ player.lastHighPriorityChecked = currentTick;
2020-08-25 04:22:08 +02:00
+ Long2IntOpenHashMap priorities = new Long2IntOpenHashMap();
2020-06-10 05:06:34 +02:00
+
+ int viewDistance = getEffectiveNoTickViewDistance();
2020-08-02 07:39:36 +02:00
+ BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition();
2020-06-10 05:06:34 +02:00
+
+ // Prioritize circular near
+ double playerChunkX = MathHelper.floor(player.locX()) >> 4;
+ double playerChunkZ = MathHelper.floor(player.locZ()) >> 4;
+ pos.setValues(player.locX(), 0, player.locZ());
2020-06-23 10:10:08 +02:00
+ double twoThirdModifier = 2D / 3D;
2020-06-10 05:06:34 +02:00
+ 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
2020-10-12 00:24:06 +02:00
+ if (dist <= 4) {
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (27 - dist));
2020-06-10 05:06:34 +02:00
+ return;
2020-05-19 10:01:53 +02:00
+ }
2020-06-10 05:06:34 +02:00
+
+ // Prioritize nearby chunks
2020-10-12 00:24:06 +02:00
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (20 - dist * twoThirdModifier));
2020-05-30 08:53:47 +02:00
+ });
+
2020-06-10 05:06:34 +02:00
+ // Prioritize Frustum near 3
+ ChunkCoordIntPair 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;
+
2020-06-23 10:10:08 +02:00
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
2020-10-12 00:24:06 +02:00
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - dist * twoThirdModifier));
2020-06-10 05:06:34 +02:00
+ });
+
+ // Prioritize Frustum near 5
+ if (viewDistance > 4) {
+ ChunkCoordIntPair front5 = player.getChunkInFront(5);
+ pos.setValues(front5.x << 4, 0, front5.z << 4);
+ MCUtil.getSpiralOutChunks(pos, 4).forEach(coord -> {
+ if (shouldSkipPrioritization(coord)) return;
2020-05-22 06:46:44 +02:00
+
2020-06-23 10:10:08 +02:00
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
2020-10-12 00:24:06 +02:00
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - dist * twoThirdModifier));
2020-05-30 08:53:47 +02:00
+ });
+ }
+
2020-06-10 05:06:34 +02:00
+ // Prioritize Frustum far 7
2020-05-30 08:53:47 +02:00
+ if (viewDistance > 6) {
2020-06-10 05:06:34 +02:00
+ ChunkCoordIntPair front7 = player.getChunkInFront(7);
+ pos.setValues(front7.x << 4, 0, front7.z << 4);
+ MCUtil.getSpiralOutChunks(pos, 3).forEach(coord -> {
+ if (shouldSkipPrioritization(coord)) {
+ return;
2020-05-22 06:46:44 +02:00
+ }
2020-06-23 10:10:08 +02:00
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
2020-10-12 00:24:06 +02:00
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - dist * twoThirdModifier));
2020-05-30 08:53:47 +02:00
+ });
+ }
+
2020-06-23 10:10:08 +02:00
+ if (priorities.isEmpty()) return;
+ chunkDistanceManager.delayDistanceManagerTick = true;
+ priorities.long2IntEntrySet().fastForEach(entry -> chunkDistanceManager.markHighPriority(new ChunkCoordIntPair(entry.getLongKey()), entry.getIntValue()));
2020-06-10 05:06:34 +02:00
+ chunkDistanceManager.delayDistanceManagerTick = false;
+ world.getChunkProvider().tickDistanceManager();
2020-06-23 10:10:08 +02:00
+
2020-05-19 10:01:53 +02:00
+ }
2020-05-30 08:53:47 +02:00
+
2020-06-10 05:06:34 +02:00
+ private boolean shouldSkipPrioritization(ChunkCoordIntPair coord) {
+ if (playerViewDistanceNoTickMap.getObjectsInRange(coord.pair()) == null) return true;
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
+ return chunk != null && (chunk.isFullChunkReady());
2020-05-30 08:53:47 +02:00
+ }
2020-05-19 10:01:53 +02:00
+ // Paper end
2020-06-27 06:57:36 +02:00
public void updatePlayerMobTypeMap(Entity entity) {
if (!this.world.paperConfig.perPlayerMobSpawns) {
2020-08-25 04:22:08 +02:00
@@ -546,6 +674,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
List<CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>>> list = Lists.newArrayList();
int j = chunkcoordintpair.x;
int k = chunkcoordintpair.z;
+ PlayerChunk requestingNeighbor = getUpdatingChunk(chunkcoordintpair.pair()); // Paper
for (int l = -i; l <= i; ++l) {
for (int i1 = -i; i1 <= i; ++i1) {
2020-08-25 04:22:08 +02:00
@@ -564,6 +693,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1);
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = playerchunk.a(chunkstatus, this);
2020-05-22 06:46:44 +02:00
+ // 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
2020-05-19 10:01:53 +02:00
list.add(completablefuture);
2020-05-22 06:46:44 +02:00
}
2020-08-25 04:22:08 +02:00
@@ -1031,14 +1168,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
};
CompletableFuture<NBTTagCompound> chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z);
+ PlayerChunk playerChunk = getUpdatingChunk(chunkcoordintpair.pair());
+ 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.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z,
- com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY, chunkHolderConsumer, false, chunkSaveFuture);
- this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY);
+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isHighestPriority, chunkSaveFuture);
} else {
- this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z,
- com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false);
+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, priority, chunkHolderConsumer, isHighestPriority);
}
+ this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, priority);
return ret;
// Paper end
}
2020-08-25 04:22:08 +02:00
@@ -1175,7 +1320,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
2020-05-19 10:01:53 +02:00
long i = playerchunk.i().pair();
playerchunk.getClass();
2020-06-26 03:58:00 +02:00
- mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getTicketLevel));
+ mailbox.a(ChunkTaskQueueSorter.a(runnable, i, () -> 1)); // Paper - final loads are always urgent!
2020-05-19 10:01:53 +02:00
});
}
2020-06-10 05:06:34 +02:00
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
2020-11-04 09:57:07 +01:00
index 37b7925b0c6dea0920d67cacf3b1982e8034e202..99b9da1f8f214082fb30070f7e853447e8ab0ccc 100644
2020-06-10 05:06:34 +02:00
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
2020-11-04 09:57:07 +01:00
@@ -1371,6 +1371,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
2020-08-07 04:49:50 +02:00
2020-06-10 05:06:34 +02:00
this.A = this.e;
this.player.setLocation(d0, d1, d2, f, f1);
2020-06-23 10:10:08 +02:00
+ this.player.forceCheckHighPriority(); // Paper
2020-06-10 05:06:34 +02:00
this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.teleportAwait));
2020-06-23 10:10:08 +02:00
}
2020-06-10 05:06:34 +02:00
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
2020-11-19 16:15:16 +01:00
index b8ab8150b7dfe9e34910d35f565083e806c4fb0c..0c71059c10dbb840c5b2aca91bc377c8db53bf52 100644
2020-06-10 05:06:34 +02:00
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
2020-06-29 00:44:34 +02:00
@@ -196,8 +196,8 @@ public abstract class PlayerList {
2020-06-10 05:06:34 +02:00
final ChunkCoordIntPair pos = new ChunkCoordIntPair(chunkX, chunkZ);
2020-06-29 00:44:34 +02:00
PlayerChunkMap playerChunkMap = worldserver1.getChunkProvider().playerChunkMap;
2020-06-10 05:06:34 +02:00
playerChunkMap.chunkDistanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
2020-06-29 00:44:34 +02:00
- worldserver1.getChunkProvider().tickDistanceManager();
- worldserver1.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
+ worldserver1.getChunkProvider().markAreaHighPriority(pos, 28, 3);
+ worldserver1.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, false).thenApply(chunk -> {
2020-06-10 05:06:34 +02:00
PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pos.pair());
if (updatingChunk != null) {
return updatingChunk.getEntityTickingFuture();
2020-10-01 15:16:32 +02:00
@@ -616,6 +616,7 @@ public abstract class PlayerList {
2020-06-23 10:10:08 +02:00
SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress();
2020-06-26 03:58:00 +02:00
EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(World.OVERWORLD), gameprofile, new PlayerInteractManager(this.server.getWorldServer(World.OVERWORLD)));
2020-06-23 10:10:08 +02:00
+ entity.isRealPlayer = true; // Paper
Player player = entity.getBukkitEntity();
PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress());
2020-10-01 15:16:32 +02:00
@@ -822,6 +823,7 @@ public abstract class PlayerList {
2020-06-10 05:06:34 +02:00
// CraftBukkit end
2020-07-17 19:24:12 +02:00
worldserver1.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
2020-06-23 10:10:08 +02:00
+ entityplayer1.forceCheckHighPriority(); // Player
2020-06-26 03:58:00 +02:00
while (avoidSuffocation && !worldserver1.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
2020-06-10 05:06:34 +02:00
entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
}
2020-05-20 11:11:57 +02:00
diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java
2020-06-26 03:58:00 +02:00
index 7a8397815a5b7f79f3e3a0348aeedf63fe879f8f..b5030d6f5d917ba33fb3c40903384fa7a56bc5f1 100644
2020-05-20 11:11:57 +02:00
--- a/src/main/java/net/minecraft/server/Ticket.java
+++ b/src/main/java/net/minecraft/server/Ticket.java
@@ -8,6 +8,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
private final int b;
public final T identifier; public final T getObjectReason() { return this.identifier; } // Paper - OBFHELPER
private long d; public final long getCreationTick() { return this.d; } // Paper - OBFHELPER
+ public int priority = 0; // Paper
protected Ticket(TicketType<T> tickettype, int i, T t0) {
this.a = tickettype;
2020-06-26 03:58:00 +02:00
@@ -56,6 +57,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
return this.b;
}
+ public final void setCurrentTick(long i) { this.a(i); } // Paper - OBFHELPER
protected void a(long i) {
this.d = i;
}
2020-05-19 10:01:53 +02:00
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
2020-06-09 09:17:25 +02:00
index d7b9d9fd3a3b607278a3d72b0b306b0be2aa30ad..6fd852db6bcfbfbf84ec2acf6d23b08a6051165c 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/net/minecraft/server/TicketType.java
+++ b/src/main/java/net/minecraft/server/TicketType.java
2020-06-09 09:17:25 +02:00
@@ -24,6 +24,8 @@ public class TicketType<T> {
2020-05-19 10:01:53 +02:00
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = a("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
public static final TicketType<Long> FUTURE_AWAIT = a("future_await", Long::compareTo); // Paper
public static final TicketType<Long> ASYNC_LOAD = a("async_load", Long::compareTo); // Paper
2020-05-22 06:46:44 +02:00
+ public static final TicketType<ChunkCoordIntPair> PRIORITY = a("priority", Comparator.comparingLong(ChunkCoordIntPair::pair), 300); // Paper
+ public static final TicketType<ChunkCoordIntPair> URGENT = a("urgent", Comparator.comparingLong(ChunkCoordIntPair::pair), 300); // Paper
2020-05-19 10:01:53 +02:00
public static <T> TicketType<T> a(String s, Comparator<T> comparator) {
return new TicketType<>(s, comparator, 0L);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
2020-08-25 04:22:08 +02:00
index f35dda50fd9015a793708d214c648d75f9f87e2b..ae1f9dde6887988ca682f53cba316ce506dbc9c1 100644
2020-05-19 10:01:53 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
2020-08-25 04:22:08 +02:00
@@ -2508,6 +2508,10 @@ public class CraftWorld implements World {
2020-05-24 00:38:29 +02:00
return future;
2020-05-19 10:01:53 +02:00
}
2020-05-22 06:46:44 +02:00
+ if (!urgent) {
+ // if not urgent, at least use a slightly boosted priority
+ world.getChunkProvider().markHighPriority(new ChunkCoordIntPair(x, z), 1);
+ }
return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
2020-05-19 10:01:53 +02:00
net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null);
return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk());
2020-06-10 05:06:34 +02:00
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2020-11-19 16:15:16 +01:00
index 708636e9ce98a8e83ffb3854e14912d135a58c1d..0697f39459ffb3905b68010541b1f200bc92ad90 100644
2020-06-10 05:06:34 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2020-10-17 12:39:45 +02:00
@@ -766,6 +766,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
2020-06-10 05:06:34 +02:00
throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
}
+ // Paper start
+ @Override
2020-07-10 01:24:10 +02:00
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location loc, @javax.annotation.Nonnull PlayerTeleportEvent.TeleportCause cause) {
+ ((CraftWorld)loc.getWorld()).getHandle().getChunkProvider().markAreaHighPriority(new net.minecraft.server.ChunkCoordIntPair(net.minecraft.server.MathHelper.floor(loc.getX()) >> 4, net.minecraft.server.MathHelper.floor(loc.getZ()) >> 4), 28, 3); // Paper - load area high priority
2020-06-10 05:06:34 +02:00
+ return super.teleportAsync(loc, cause);
+ }
+ // Paper end
+
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
Preconditions.checkArgument(location != null, "location");