mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-04 14:04:49 +01:00
c4ada0e199
Fixes a few various issues with chunk ticket state restores mojangs ticket throttle but tries to be smarter about it. fixes a few state mismatches that needed to be handled. Fixes fake NPC's adding player tickets when they shouldn't have been. Improves teleport chunk loading by processing high priority on new area Fixes #3605 Fixes #3537 Fixes #3573
1222 lines
64 KiB
Diff
1222 lines
64 KiB
Diff
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
|
|
index b5c2e1f4a2b5fdcaa6bb01f4b3b6847cd5b73ae8..6209b33d8497ec56bbde507e523db0649c66f590 100644
|
|
--- 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;
|
|
@@ -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 {
|
|
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());
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
|
index f617636a22167b06ac8073aa25efd8c7099155f0..0f40793f004639822b9d40521cd21ec50391ba3b 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
|
|
@@ -110,6 +110,7 @@ public class ChunkCoordIntPair {
|
|
return "[" + this.x + ", " + this.z + "]";
|
|
}
|
|
|
|
+ public final BlockPosition asPosition() { return l(); } // Paper - OBFHELPER
|
|
public BlockPosition l() {
|
|
return new BlockPosition(this.x << 4, 0, this.z << 4);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..35a4999b2c34ae62cba042885db25dd1837cb127 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
@@ -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 {
|
|
|
|
@@ -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();
|
|
+ private final LongSet l = new LongOpenHashSet(); LongSet getOnPlayerTicketAddQueue() { return l; } // Paper - OBFHELPER
|
|
private final Executor m;
|
|
private long currentTick;
|
|
|
|
@@ -84,6 +85,7 @@ public abstract class ChunkMapDistance {
|
|
}
|
|
|
|
private static int a(ArraySetSorted<Ticket<?>> arraysetsorted) {
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::getHighestTicketLevel"); // Paper
|
|
return !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.b()).b() : PlayerChunkMap.GOLDEN_TICKET + 1;
|
|
}
|
|
|
|
@@ -97,6 +99,7 @@ public abstract class ChunkMapDistance {
|
|
|
|
public boolean a(PlayerChunkMap playerchunkmap) {
|
|
//this.f.a(); // Paper - no longer used
|
|
+ AsyncCatcher.catchOp("DistanceManagerTick");
|
|
this.g.a();
|
|
int i = Integer.MAX_VALUE - this.e.a(Integer.MAX_VALUE);
|
|
boolean flag = i != 0;
|
|
@@ -107,11 +110,13 @@ public abstract class ChunkMapDistance {
|
|
|
|
// Paper start
|
|
if (!this.pendingChunkUpdates.isEmpty()) {
|
|
+ this.pollingPendingChunkUpdates = true; try {
|
|
while(!this.pendingChunkUpdates.isEmpty()) {
|
|
PlayerChunk remove = this.pendingChunkUpdates.remove();
|
|
remove.isUpdateQueued = false;
|
|
remove.a(playerchunkmap);
|
|
}
|
|
+ } finally { this.pollingPendingChunkUpdates = false; }
|
|
// Paper end
|
|
return true;
|
|
} else {
|
|
@@ -147,8 +152,10 @@ public abstract class ChunkMapDistance {
|
|
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);
|
|
int j = a(arraysetsorted);
|
|
Ticket<?> ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error
|
|
@@ -162,7 +169,9 @@ public abstract class ChunkMapDistance {
|
|
}
|
|
|
|
private boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
|
|
ArraySetSorted<Ticket<?>> arraysetsorted = this.e(i);
|
|
+ int oldLevel = a(arraysetsorted); // Paper
|
|
|
|
boolean removed = false; // CraftBukkit
|
|
if (arraysetsorted.remove(ticket)) {
|
|
@@ -172,8 +181,8 @@ public abstract class ChunkMapDistance {
|
|
if (arraysetsorted.isEmpty()) {
|
|
this.tickets.remove(i);
|
|
}
|
|
-
|
|
- this.e.b(i, a(arraysetsorted), false);
|
|
+ int newLevel = a(arraysetsorted); // Paper
|
|
+ if (newLevel > oldLevel) this.e.b(i, newLevel, false); // Paper
|
|
return removed; // CraftBukkit
|
|
}
|
|
|
|
@@ -182,6 +191,135 @@ public abstract class ChunkMapDistance {
|
|
this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0);
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public static final int PRIORITY_TICKET_LEVEL = PlayerChunkMap.GOLDEN_TICKET;
|
|
+ public static final int URGENT_PRIORITY = 29;
|
|
+ public boolean delayDistanceManagerTick = false;
|
|
+ public boolean markUrgent(ChunkCoordIntPair coords) {
|
|
+ return addPriorityTicket(coords, TicketType.URGENT, URGENT_PRIORITY);
|
|
+ }
|
|
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
|
|
+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority));
|
|
+ return addPriorityTicket(coords, TicketType.PRIORITY, priority);
|
|
+ }
|
|
+
|
|
+ public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) {
|
|
+ delayDistanceManagerTick = true;
|
|
+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority));
|
|
+ int finalPriority = priority;
|
|
+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> {
|
|
+ addPriorityTicket(coords, TicketType.PRIORITY, finalPriority);
|
|
+ });
|
|
+ delayDistanceManagerTick = false;
|
|
+ chunkMap.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();
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ private boolean addPriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> ticketType, int priority) {
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket");
|
|
+ long pair = coords.pair();
|
|
+ PlayerChunk chunk = chunkMap.getUpdatingChunk(pair);
|
|
+ boolean needsTicket = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null && !hasPlayerTicket(coords, 33);
|
|
+
|
|
+ if (needsTicket) {
|
|
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords);
|
|
+ getOnPlayerTicketAddQueue().add(pair);
|
|
+ addTicket(pair, ticket);
|
|
+ }
|
|
+ if ((chunk != null && chunk.isFullChunkReady())) {
|
|
+ if (needsTicket) {
|
|
+ chunkMap.world.getChunkProvider().tickDistanceManager();
|
|
+ }
|
|
+ return needsTicket;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ } else {
|
|
+ if (chunk == null) {
|
|
+ chunk = chunkMap.getUpdatingChunk(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.world.getChunkProvider().tickDistanceManager();
|
|
+
|
|
+ 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
|
|
+ ticket.setCurrentTick(this.currentTick);
|
|
+ ticket.priority = Math.max(ticket.priority, priority);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public int getChunkPriority(ChunkCoordIntPair coords) {
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::getChunkPriority");
|
|
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
|
|
+ if (tickets == null) {
|
|
+ return 0;
|
|
+ }
|
|
+ for (Ticket<?> ticket : tickets) {
|
|
+ if (ticket.getTicketType() == TicketType.URGENT) {
|
|
+ return URGENT_PRIORITY;
|
|
+ }
|
|
+ }
|
|
+ for (Ticket<?> ticket : tickets) {
|
|
+ if (ticket.getTicketType() == TicketType.PRIORITY && ticket.priority > 0) {
|
|
+ return ticket.priority;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::clearPriority");
|
|
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.PRIORITY, PRIORITY_TICKET_LEVEL, coords));
|
|
+ }
|
|
+
|
|
+ public void clearUrgent(ChunkCoordIntPair coords) {
|
|
+ AsyncCatcher.catchOp("ChunkMapDistance::clearUrgent");
|
|
+ this.removeTicket(coords.pair(), new Ticket<ChunkCoordIntPair>(TicketType.URGENT, PRIORITY_TICKET_LEVEL, coords));
|
|
+ }
|
|
+ // 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
|
|
@@ -381,27 +519,51 @@ public abstract class ChunkMapDistance {
|
|
|
|
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
|
|
|
|
if (flag1) {
|
|
+ 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
|
|
ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
|
|
ChunkMapDistance.this.m.execute(() -> {
|
|
- if (this.c(this.c(i))) {
|
|
+ if (isChunkInRange(i)) { if (!hasPlayerTicket(coords, 33)) { // Paper - high priority might of already added it
|
|
ChunkMapDistance.this.addTicket(i, ticket);
|
|
ChunkMapDistance.this.l.add(i);
|
|
- } else {
|
|
+ }
|
|
+ } else { // Paper
|
|
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
|
|
}, i, false));
|
|
}
|
|
|
|
});
|
|
}, i, () -> {
|
|
- return j;
|
|
+ return Math.min(PlayerChunkMap.GOLDEN_TICKET, priority); // Paper
|
|
}));
|
|
+ }); // Paper
|
|
} else {
|
|
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
|
|
ChunkMapDistance.this.m.execute(() -> {
|
|
ChunkMapDistance.this.removeTicket(i, ticket);
|
|
+ ChunkMapDistance.this.clearPriorityTickets(coords); // Paper
|
|
});
|
|
}, i, true));
|
|
}
|
|
@@ -409,6 +571,102 @@ public abstract class ChunkMapDistance {
|
|
|
|
}
|
|
|
|
+ // Paper start - smart scheduling of player tickets
|
|
+ private boolean isChunkInRange(long i) {
|
|
+ return this.isLoadedChunkLevel(this.getChunkLevel(i));
|
|
+ }
|
|
+ public void scheduleChunkLoad(long i, long startTick, int initialDistance, java.util.function.Consumer<Integer> task) {
|
|
+ long elapsed = MinecraftServer.currentTick - startTick;
|
|
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i);
|
|
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(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<EntityPlayer> 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();
|
|
+
|
|
+ BlockPosition blockPos = chunkPos.asPosition();
|
|
+
|
|
+ boolean isFront = false;
|
|
+ BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire();
|
|
+ for (int index = 0, len = backingSet.length; index < len; ++index) {
|
|
+ if (!(backingSet[index] instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer) backingSet[index];
|
|
+
|
|
+ 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());
|
|
+ double center = MCUtil.distanceSq(pos, blockPos);
|
|
+
|
|
+ double dist = Math.min(frontDist, center);
|
|
+ if (!isFront) {
|
|
+ ChunkCoordIntPair pointInBack = player.getChunkInFront(-7);
|
|
+ pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4);
|
|
+ double backDist = MCUtil.distanceSq(pos, blockPos);
|
|
+ if (frontDist < backDist) {
|
|
+ isFront = true;
|
|
+ }
|
|
+ }
|
|
+ if (dist < minDist) {
|
|
+ minDist = dist;
|
|
+ }
|
|
+ }
|
|
+ pos.close();
|
|
+ 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 = ChunkCoordIntPair.pair(chunkPos.x + x, chunkPos.z + z);
|
|
+ PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair);
|
|
+ ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null;
|
|
+ if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) {
|
|
+ hasAnyNeighbor = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!hasAnyNeighbor) {
|
|
+ delay += 20;
|
|
+ }
|
|
+ }
|
|
+ if (delay <= 0) {
|
|
+ task.accept((int) minDist);
|
|
+ } else {
|
|
+ int taskDelay = (int) Math.min(delay, minDist >= 10 ? 40 : (minDist < 6 ? 5 : 20));
|
|
+ MCUtil.scheduleTask(taskDelay, () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer");
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
@Override
|
|
public void a() {
|
|
super.a();
|
|
@@ -440,6 +698,7 @@ public abstract class ChunkMapDistance {
|
|
|
|
}
|
|
|
|
+ private boolean isLoadedChunkLevel(int i) { return c(i); } // Paper - OBFHELPER
|
|
private boolean c(int i) {
|
|
return i <= this.e - 2;
|
|
}
|
|
@@ -456,6 +715,7 @@ public abstract class ChunkMapDistance {
|
|
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);
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 7a275bf3260f9fbefc41883c5ebdc1eb2196daf0..b6a7e475c6ebe499c641db5967adb1674323a517 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -432,6 +432,26 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
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) {
|
|
+ return this.chunkMapDistance.markUrgent(coords);
|
|
+ }
|
|
+
|
|
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
|
|
+ return this.chunkMapDistance.markHighPriority(coords, priority);
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
|
|
+ this.chunkMapDistance.clearPriorityTickets(coords);
|
|
+ }
|
|
// Paper end
|
|
|
|
@Nullable
|
|
@@ -470,6 +490,8 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
|
|
if (!completablefuture.isDone()) { // Paper
|
|
// Paper start - async chunk io/loading
|
|
+ ChunkCoordIntPair pair = new ChunkCoordIntPair(x, z);
|
|
+ this.chunkMapDistance.markUrgent(pair);
|
|
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
|
|
@@ -478,6 +500,8 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
this.serverThreadQueue.awaitTasks(completablefuture::isDone);
|
|
com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug
|
|
this.world.timings.syncChunkLoad.stopTiming(); // Paper
|
|
+ this.chunkMapDistance.clearPriorityTickets(pair); // Paper
|
|
+ this.chunkMapDistance.clearUrgent(pair); // Paper
|
|
} // Paper
|
|
ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
|
return ichunkaccess1;
|
|
@@ -530,10 +554,12 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
if (flag && !currentlyUnloading) {
|
|
// CraftBukkit end
|
|
this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
|
|
+ if (isUrgent) this.chunkMapDistance.markUrgent(chunkcoordintpair); // Paper
|
|
if (this.a(playerchunk, l)) {
|
|
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
|
|
|
|
gameprofilerfiller.enter("chunkLoad");
|
|
+ chunkMapDistance.delayDistanceManagerTick = false; // Paper - ensure this is never false
|
|
this.tickDistanceManager();
|
|
playerchunk = this.getChunk(k);
|
|
gameprofilerfiller.exit();
|
|
@@ -542,8 +568,13 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
}
|
|
}
|
|
}
|
|
-
|
|
- 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) {
|
|
+ future.thenAccept(either -> this.chunkMapDistance.clearUrgent(chunkcoordintpair));
|
|
+ }
|
|
+ return future;
|
|
+ // Paper end
|
|
}
|
|
|
|
private boolean a(@Nullable PlayerChunk playerchunk, int i) {
|
|
@@ -593,7 +624,8 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
return this.serverThreadQueue.executeNext();
|
|
}
|
|
|
|
- private boolean tickDistanceManager() {
|
|
+ public boolean tickDistanceManager() { // Paper - public
|
|
+ if (chunkMapDistance.delayDistanceManagerTick) return false; // Paper
|
|
boolean flag = this.chunkMapDistance.a(this.playerChunkMap);
|
|
boolean flag1 = this.playerChunkMap.b();
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
index 07a6fc3d88e7d44bfab7f3d6a0eef7dc132ab422..39072ebdc9e4be2bb762d81a8bd542dbdb8f6371 100644
|
|
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
@@ -55,6 +55,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|
private int lastArmorScored = Integer.MIN_VALUE;
|
|
private int lastExpLevelScored = Integer.MIN_VALUE;
|
|
private int lastExpTotalScored = Integer.MIN_VALUE;
|
|
+ public long lastHighPriorityChecked; // Paper
|
|
+ public void forceCheckHighPriority() {
|
|
+ lastHighPriorityChecked = -1;
|
|
+ getWorldServer().getChunkProvider().playerChunkMap.checkHighPriorityChunks(this);
|
|
+ }
|
|
+ public boolean isRealPlayer; // Paper
|
|
private float lastHealthSent = -1.0E8F;
|
|
private int lastFoodSent = -99999999;
|
|
private boolean lastSentSaturationZero = true;
|
|
@@ -132,6 +138,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|
this.maxHealthCache = this.getMaxHealth();
|
|
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
|
|
}
|
|
+ // Paper start
|
|
+ public BlockPosition getPointInFront(double inFront) {
|
|
+ double rads = Math.toRadians(MCUtil.normalizeYaw(this.yaw+90)); // MC rotates yaw 90 for some odd reason
|
|
+ final double x = locX() + inFront * Math.cos(rads);
|
|
+ final double z = locZ() + inFront * Math.sin(rads);
|
|
+ return new BlockPosition(x, locY(), z);
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+ // 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
|
|
@@ -441,6 +462,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|
if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
|
|
super.tick();
|
|
}
|
|
+ if (valid && isAlive() && playerConnection != null) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper
|
|
|
|
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
|
|
index ce0bf608b71cf492fc31e89a360ecd83fa5c23a6..87d58002116f361d8255d79fc0dbd1200f442168 100644
|
|
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
|
+++ b/src/main/java/net/minecraft/server/MCUtil.java
|
|
@@ -666,6 +666,7 @@ public final class MCUtil {
|
|
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
|
|
index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..52da9c78c0cc2b21533a1477a25a3dda492700e4 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -26,8 +26,8 @@ public class PlayerChunk {
|
|
private CompletableFuture<IChunkAccess> chunkSave;
|
|
public int oldTicketLevel;
|
|
private int ticketLevel;
|
|
- private int n;
|
|
- 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
|
|
private final short[] dirtyBlocks;
|
|
private int dirtyCount;
|
|
private int r;
|
|
@@ -40,6 +40,7 @@ public class PlayerChunk {
|
|
private boolean hasBeenLoaded;
|
|
|
|
private final PlayerChunkMap chunkMap; // Paper
|
|
+ public WorldServer getWorld() { return chunkMap.world; } // Paper
|
|
|
|
long lastAutoSaveTime; // Paper - incremental autosave
|
|
long inactiveTimeStart; // Paper - incremental autosave
|
|
@@ -67,6 +68,120 @@ public class PlayerChunk {
|
|
return null;
|
|
}
|
|
// Paper end - no-tick view distance
|
|
+ // Paper start - Chunk gen/load priority system
|
|
+ volatile int neighborPriority = -1;
|
|
+ volatile int priorityBoost = 0;
|
|
+ public final java.util.concurrent.ConcurrentHashMap<PlayerChunk, ChunkStatus> neighbors = new java.util.concurrent.ConcurrentHashMap<>();
|
|
+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<Integer> neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
+
|
|
+ private int getDemandedPriority() {
|
|
+ int priority = neighborPriority; // if we have a neighbor priority, use it
|
|
+ int myPriority = getMyPriority();
|
|
+
|
|
+ if (priority == -1 || (ticketLevel <= 33 && priority > myPriority)) {
|
|
+ priority = myPriority;
|
|
+ }
|
|
+
|
|
+ return Math.max(1, Math.min(Math.max(ticketLevel, PlayerChunkMap.GOLDEN_TICKET), priority));
|
|
+ }
|
|
+
|
|
+ private int getMyPriority() {
|
|
+ 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
|
|
+ }
|
|
+ return ticketLevel - priorityBoost;
|
|
+ }
|
|
+
|
|
+ private int getNeighborsPriority() {
|
|
+ return neighborPriorities.isEmpty() ? getMyPriority() : getDemandedPriority();
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ });
|
|
+
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ private void removeNeighborPriority(PlayerChunk requester) {
|
|
+ synchronized (neighborPriorities) {
|
|
+ neighborPriorities.remove(requester.location.pair());
|
|
+ recalcNeighborPriority();
|
|
+ }
|
|
+ checkPriority();
|
|
+ }
|
|
+
|
|
+
|
|
+ private void setNeighborPriority(PlayerChunk requester, int priority) {
|
|
+ synchronized (neighborPriorities) {
|
|
+ neighborPriorities.put(requester.location.pair(), Integer.valueOf(priority));
|
|
+ recalcNeighborPriority();
|
|
+ }
|
|
+ checkPriority();
|
|
+ }
|
|
+
|
|
+ private void recalcNeighborPriority() {
|
|
+ neighborPriority = -1;
|
|
+ if (!neighborPriorities.isEmpty()) {
|
|
+ synchronized (neighborPriorities) {
|
|
+ for (Integer neighbor : neighborPriorities.values()) {
|
|
+ if (neighbor < neighborPriority || neighborPriority == -1) {
|
|
+ neighborPriority = neighbor;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ private void checkPriority() {
|
|
+ if (getCurrentPriority() != getDemandedPriority()) this.chunkMap.queueHolderUpdate(this);
|
|
+ }
|
|
+
|
|
+ public final double getDistance(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);
|
|
+ }
|
|
+
|
|
+ public final double getDistanceFrom(BlockPosition pos) {
|
|
+ return getDistance(pos.getX(), pos.getZ());
|
|
+ }
|
|
+
|
|
+ @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() +
|
|
+ '}';
|
|
+ }
|
|
+ // 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());
|
|
@@ -165,6 +280,18 @@ public class PlayerChunk {
|
|
}
|
|
return null;
|
|
}
|
|
+ public static ChunkStatus getNextStatus(ChunkStatus status) {
|
|
+ if (status == ChunkStatus.FULL) {
|
|
+ return status;
|
|
+ }
|
|
+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1);
|
|
+ }
|
|
+ public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) {
|
|
+ return ensureMain(getStatusFutureUnchecked(chunkstatus));
|
|
+ }
|
|
+ public <T> CompletableFuture<T> ensureMain(CompletableFuture<T> future) {
|
|
+ return future.thenApplyAsync(r -> r, chunkMap.mainInvokingExecutor);
|
|
+ }
|
|
// Paper end
|
|
|
|
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
|
|
@@ -418,6 +545,7 @@ public class PlayerChunk {
|
|
return this.n;
|
|
}
|
|
|
|
+ private void setPriority(int i) { d(i); } // Paper - OBFHELPER
|
|
private void d(int i) {
|
|
this.n = i;
|
|
}
|
|
@@ -436,7 +564,7 @@ public class PlayerChunk {
|
|
// 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(() -> {
|
|
@@ -501,12 +629,13 @@ public class PlayerChunk {
|
|
if (!flag2 && flag3) {
|
|
// Paper start - cache ticking ready status
|
|
int expectCreateCount = ++this.fullChunkCreateCount;
|
|
- this.fullChunkFuture = playerchunkmap.b(this); this.fullChunkFuture.thenAccept((either) -> {
|
|
+ this.fullChunkFuture = playerchunkmap.b(this); ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main
|
|
if (either.left().isPresent() && PlayerChunk.this.fullChunkCreateCount == expectCreateCount) {
|
|
// note: Here is a very good place to add callbacks to logic waiting on this.
|
|
Chunk fullChunk = either.left().get();
|
|
PlayerChunk.this.isFullChunkReady = true;
|
|
fullChunk.playerChunk = PlayerChunk.this;
|
|
+ this.chunkMap.chunkDistanceManager.clearPriorityTickets(location);
|
|
|
|
|
|
}
|
|
@@ -531,7 +660,7 @@ public class PlayerChunk {
|
|
|
|
if (!flag4 && flag5) {
|
|
// Paper start - cache ticking ready status
|
|
- this.tickingFuture = playerchunkmap.a(this); this.tickingFuture.thenAccept((either) -> {
|
|
+ this.tickingFuture = playerchunkmap.a(this); ensureMain(this.tickingFuture).thenAccept((either) -> { // Paper - ensure main
|
|
if (either.left().isPresent()) {
|
|
// note: Here is a very good place to add callbacks to logic waiting on this.
|
|
Chunk tickingChunk = either.left().get();
|
|
@@ -562,7 +691,7 @@ public class PlayerChunk {
|
|
}
|
|
|
|
// Paper start - cache ticking ready status
|
|
- this.entityTickingFuture = playerchunkmap.b(this.location); this.entityTickingFuture.thenAccept((either) -> {
|
|
+ this.entityTickingFuture = playerchunkmap.b(this.location); ensureMain(this.entityTickingFuture).thenAccept((either) -> { // Paper ensureMain
|
|
if (either.left().isPresent()) {
|
|
// note: Here is a very good place to add callbacks to logic waiting on this.
|
|
Chunk entityTickingChunk = either.left().get();
|
|
@@ -581,13 +710,29 @@ public class PlayerChunk {
|
|
this.entityTickingFuture.complete(PlayerChunk.UNLOADED_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
|
|
this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
|
|
}
|
|
-
|
|
- this.w.a(this.location, this::k, this.ticketLevel, this::d);
|
|
+ // Paper start - raise IO/load priority if priority changes, use our preferred priority
|
|
+ priorityBoost = chunkMap.chunkDistanceManager.getChunkPriority(location);
|
|
+ int priority = getDemandedPriority();
|
|
+ if (getCurrentPriority() > priority) {
|
|
+ int ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY;
|
|
+ if (priority <= 10) {
|
|
+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY;
|
|
+ } else if (priority <= 20) {
|
|
+ ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY;
|
|
+ }
|
|
+ chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority);
|
|
+ }
|
|
+ if (getCurrentPriority() != priority) {
|
|
+ this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority
|
|
+ int neighborsPriority = getNeighborsPriority();
|
|
+ this.neighbors.forEach((neighbor, neighborDesired) -> neighbor.setNeighborPriority(this, neighborsPriority));
|
|
+ }
|
|
+ // Paper end
|
|
this.oldTicketLevel = this.ticketLevel;
|
|
// CraftBukkit start
|
|
// ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
|
|
if (!playerchunk_state.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(() -> {
|
|
@@ -669,6 +814,7 @@ public class PlayerChunk {
|
|
|
|
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
|
|
index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..3139d55bb801b6c433de7274be4a1d0c0fd1eef7 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -50,6 +50,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
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 {
|
|
|
|
@@ -123,6 +124,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
@Override
|
|
public void execute(Runnable runnable) {
|
|
+ AsyncCatcher.catchOp("Callback Executor execute");
|
|
if (queued == null) {
|
|
queued = new java.util.ArrayDeque<>();
|
|
}
|
|
@@ -131,6 +133,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
@Override
|
|
public void run() {
|
|
+ AsyncCatcher.catchOp("Callback Executor run");
|
|
if (queued == null) {
|
|
return;
|
|
}
|
|
@@ -375,6 +378,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
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;
|
|
}
|
|
@@ -393,7 +397,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
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);
|
|
+ }, (player, prevPos, newPos) -> {
|
|
+ player.lastHighPriorityChecked = -1; // reset and recheck
|
|
+ checkHighPriorityChunks(player);
|
|
+ });
|
|
this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
|
this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
(EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
@@ -410,6 +418,116 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
});
|
|
// Paper end - no-tick view distance
|
|
}
|
|
+ // Paper start - Chunk Prioritization
|
|
+ public void queueHolderUpdate(PlayerChunk playerchunk) {
|
|
+ Runnable runnable = () -> {
|
|
+ if (isUnloading(playerchunk)) {
|
|
+ return; // unloaded
|
|
+ }
|
|
+ chunkDistanceManager.pendingChunkUpdates.add(playerchunk);
|
|
+ if (!chunkDistanceManager.pollingPendingChunkUpdates) {
|
|
+ world.getChunkProvider().tickDistanceManager();
|
|
+ }
|
|
+ };
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private boolean isUnloading(PlayerChunk playerchunk) {
|
|
+ return playerchunk == null || unloadQueue.contains(playerchunk.location.pair());
|
|
+ }
|
|
+
|
|
+ private void updateChunkPriorityMap(it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap map, long chunk, int level) {
|
|
+ int prev = map.getOrDefault(chunk, -1);
|
|
+ if (level > prev) {
|
|
+ map.put(chunk, level);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void checkHighPriorityChunks(EntityPlayer player) {
|
|
+ int currentTick = MinecraftServer.currentTick;
|
|
+ if (currentTick - player.lastHighPriorityChecked < 20 || !player.isRealPlayer) { // weed out fake players
|
|
+ return;
|
|
+ }
|
|
+ player.lastHighPriorityChecked = currentTick;
|
|
+ it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap priorities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap();
|
|
+
|
|
+ int viewDistance = getEffectiveNoTickViewDistance();
|
|
+ BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire();
|
|
+
|
|
+ // Prioritize circular near
|
|
+ double playerChunkX = MathHelper.floor(player.locX()) >> 4;
|
|
+ double playerChunkZ = MathHelper.floor(player.locZ()) >> 4;
|
|
+ pos.setValues(player.locX(), 0, player.locZ());
|
|
+ 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 * 4) {
|
|
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (27 - Math.sqrt(dist)));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Prioritize nearby chunks
|
|
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (20 - Math.sqrt(dist) * twoThirdModifier));
|
|
+ });
|
|
+
|
|
+ // 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;
|
|
+
|
|
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
|
|
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - Math.sqrt(dist) * twoThirdModifier));
|
|
+ });
|
|
+
|
|
+ // 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;
|
|
+
|
|
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
|
|
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - Math.sqrt(dist) * twoThirdModifier));
|
|
+ });
|
|
+ }
|
|
+
|
|
+ // Prioritize Frustum far 7
|
|
+ if (viewDistance > 6) {
|
|
+ ChunkCoordIntPair front7 = player.getChunkInFront(7);
|
|
+ pos.setValues(front7.x << 4, 0, front7.z << 4);
|
|
+ MCUtil.getSpiralOutChunks(pos, 3).forEach(coord -> {
|
|
+ if (shouldSkipPrioritization(coord)) {
|
|
+ return;
|
|
+ }
|
|
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
|
|
+ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - Math.sqrt(dist) * twoThirdModifier));
|
|
+ });
|
|
+ }
|
|
+
|
|
+ pos.close();
|
|
+ if (priorities.isEmpty()) return;
|
|
+ chunkDistanceManager.delayDistanceManagerTick = true;
|
|
+ priorities.long2IntEntrySet().fastForEach(entry -> chunkDistanceManager.markHighPriority(new ChunkCoordIntPair(entry.getLongKey()), entry.getIntValue()));
|
|
+ chunkDistanceManager.delayDistanceManagerTick = false;
|
|
+ world.getChunkProvider().tickDistanceManager();
|
|
+
|
|
+ }
|
|
+
|
|
+ private boolean shouldSkipPrioritization(ChunkCoordIntPair coord) {
|
|
+ if (playerViewDistanceNoTickMap.getObjectsInRange(coord.pair()) == null) return true;
|
|
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
|
|
+ return chunk != null && (chunk.isFullChunkReady());
|
|
+ }
|
|
+ // Paper end
|
|
|
|
public void updatePlayerMobTypeMap(Entity entity) {
|
|
if (!this.world.paperConfig.perPlayerMobSpawns) {
|
|
@@ -539,6 +657,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
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) {
|
|
@@ -557,6 +676,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1);
|
|
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = playerchunk.a(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);
|
|
}
|
|
@@ -1022,14 +1149,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
};
|
|
|
|
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
|
|
}
|
|
@@ -1158,7 +1293,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
long i = playerchunk.i().pair();
|
|
|
|
playerchunk.getClass();
|
|
- mailbox.a(ChunkTaskQueueSorter.a(runnable, i, playerchunk::getTicketLevel)); // CraftBukkit - decompile error
|
|
+ mailbox.a(ChunkTaskQueueSorter.a(runnable, i, () -> 1)); // CraftBukkit - decompile error // Paper - final loads are always urgent!
|
|
});
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
index d52fbda79fe1c52d3ddb53c0f1c1f521d7620702..f9cb87a3be35575ecf3362b10dc7fe5ebadb56ec 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
@@ -1279,6 +1279,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
|
|
this.A = this.e;
|
|
this.player.setLocation(d0, d1, d2, f, f1);
|
|
this.syncPosition(); // Paper
|
|
+ this.player.forceCheckHighPriority(); // Paper
|
|
this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.teleportAwait));
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
|
|
index 6daca5c0ffd1d84f9a25cd106e8992a055dfb912..f133e7baf958b031819c2c72ccd21c52ba9a683d 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerList.java
|
|
@@ -174,8 +174,8 @@ public abstract class PlayerList {
|
|
final ChunkCoordIntPair pos = new ChunkCoordIntPair(chunkX, chunkZ);
|
|
PlayerChunkMap playerChunkMap = finalWorldserver.getChunkProvider().playerChunkMap;
|
|
playerChunkMap.chunkDistanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
|
|
- worldserver.getChunkProvider().tickDistanceManager();
|
|
- worldserver.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, true).thenApply(chunk -> {
|
|
+ worldserver.getChunkProvider().markAreaHighPriority(pos, 28, 3);
|
|
+ worldserver.getChunkProvider().getChunkAtAsynchronously(chunkX, chunkZ, true, false).thenApply(chunk -> {
|
|
PlayerChunk updatingChunk = playerChunkMap.getUpdatingChunk(pos.pair());
|
|
if (updatingChunk != null) {
|
|
return updatingChunk.getEntityTickingFuture();
|
|
@@ -188,7 +188,6 @@ public abstract class PlayerList {
|
|
entityplayer, finalWorldserver, networkmanager, playerconnection,
|
|
nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
|
|
);
|
|
- //playerChunkMap.chunkDistanceManager.removeTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
|
|
};
|
|
});
|
|
}
|
|
@@ -585,6 +584,7 @@ public abstract class PlayerList {
|
|
SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress();
|
|
|
|
EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(DimensionManager.OVERWORLD), gameprofile, new PlayerInteractManager(this.server.getWorldServer(DimensionManager.OVERWORLD)));
|
|
+ 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());
|
|
|
|
@@ -764,6 +764,7 @@ public abstract class PlayerList {
|
|
// CraftBukkit end
|
|
|
|
worldserver.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
|
|
+ entityplayer1.forceCheckHighPriority(); // Player
|
|
while (avoidSuffocation && !worldserver.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
|
|
entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java
|
|
index 7a8397815a5b7f79f3e3a0348aeedf63fe879f8f..0d6e0f2ddaa85c04e626980591e9a78ac27fb42d 100644
|
|
--- 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;
|
|
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
|
|
index d7b9d9fd3a3b607278a3d72b0b306b0be2aa30ad..6fd852db6bcfbfbf84ec2acf6d23b08a6051165c 100644
|
|
--- a/src/main/java/net/minecraft/server/TicketType.java
|
|
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
|
@@ -24,6 +24,8 @@ public class TicketType<T> {
|
|
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
|
|
+ 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
|
|
|
|
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
|
|
index 5f180bb77b736220c848357b2cac4d0d2b99e3df..d3c5b7d1904a6cbd65db406639ed2ba90ec9fd2a 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -2484,6 +2484,10 @@ public class CraftWorld implements World {
|
|
return future;
|
|
}
|
|
|
|
+ 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) -> {
|
|
net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null);
|
|
return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk());
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
index bd4d5184b607db09c8ff2687ceaf47fb94368a28..141003ca718cc6ba113de8e4855b18252b293ef6 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
@@ -738,6 +738,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
|
|
}
|
|
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Location loc, PlayerTeleportEvent.TeleportCause cause) {
|
|
+ getHandle().getWorldServer().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
|
|
+ return super.teleportAsync(loc, cause);
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
@Override
|
|
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
|
|
Preconditions.checkArgument(location != null, "location");
|