mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-01 17:01:56 +01:00
e14f7e171f
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. This commit also improves single core cpu scenarios in that we will now automatically disable Async Chunks as well as Minecrafts thread pool. It is never recommended to use async chunks on a single CPU as context switching will be slower than just running it all on main. This also bumps the number of server worker threads by default too. Mojang does not utilize the workers in an effecient manner, resulting in them using barely any sustained CPU. So give it more workers so more chunks can be processed concurrently This change also improves urgent chunk loading, so players flying into unloaded chunks will hurt a little bit less (but still hurt) Ping #3395 #3363 (Not marking as closed, we need to make prevent moving work)
524 lines
27 KiB
Diff
524 lines
27 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/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
|
|
Ticket<?> ticket1 = (Ticket) arraysetsorted.a(ticket); // CraftBukkit - decompile error
|
|
|
|
ticket1.a(this.currentTick);
|
|
- if (ticket.b() < j) {
|
|
+ if (ticket.b() < j || (ticket.getTicketType() == TicketType.PRIORITY && ((Ticket<Integer>) ticket).getObjectReason() < j)) { // Paper - check priority tickets too
|
|
this.e.b(i, ticket.b(), true);
|
|
}
|
|
|
|
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
|
|
this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0);
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public boolean markUrgent(ChunkCoordIntPair coords) {
|
|
+ return this.markHighPriority(coords, 30);
|
|
+ }
|
|
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
|
|
+ priority = Math.min(30, Math.max(1, priority));
|
|
+ Ticket<Integer> ticket = new Ticket<Integer>(TicketType.PRIORITY, 31, priority);
|
|
+ return this.addTicket(coords.pair(), ticket);
|
|
+ }
|
|
+ public int getChunkPriority(ChunkCoordIntPair coords) {
|
|
+ int priority = 0;
|
|
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
|
|
+ if (tickets == null) {
|
|
+ return priority;
|
|
+ }
|
|
+ for (Ticket<?> ticket : tickets) {
|
|
+ if (ticket.getTicketType() != TicketType.PRIORITY) {
|
|
+ continue;
|
|
+ }
|
|
+ //noinspection unchecked
|
|
+ Ticket<Integer> prioTicket = (Ticket<Integer>) ticket;
|
|
+ if (prioTicket.getObjectReason() > priority) {
|
|
+ priority = prioTicket.getObjectReason();
|
|
+ }
|
|
+ }
|
|
+ return priority;
|
|
+ }
|
|
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
|
|
+ ArraySetSorted<Ticket<?>> tickets = this.tickets.get(coords.pair());
|
|
+ java.util.List<Ticket<?>> toRemove = new java.util.ArrayList<>();
|
|
+ if (tickets == null) return;
|
|
+ for (Ticket<?> ticket : tickets) {
|
|
+ if (ticket.getTicketType() == TicketType.PRIORITY) {
|
|
+ toRemove.add(ticket);
|
|
+ }
|
|
+ }
|
|
+ for (Ticket<?> ticket : toRemove) {
|
|
+ this.removeTicket(coords.pair(), ticket);
|
|
+ }
|
|
+
|
|
+ }
|
|
+ // 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
|
|
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
|
|
|
|
});
|
|
}, i, () -> {
|
|
- return j;
|
|
+ PlayerChunk chunk = chunkMap.getUpdatingChunk(i); // Paper
|
|
+ return chunk != null && chunk.getCurrentPriority() < j ? chunk.getCurrentPriority() : j; // Paper
|
|
}));
|
|
} else {
|
|
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -0,0 +0,0 @@ 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 chunkMapDistance.markUrgent(coords);
|
|
+ }
|
|
+ public boolean markHighPriority(ChunkCoordIntPair coords, int priority) {
|
|
+ return chunkMapDistance.markHighPriority(coords, priority);
|
|
+ }
|
|
+ public void clearPriorityTickets(ChunkCoordIntPair coords) {
|
|
+ this.chunkMapDistance.clearPriorityTickets(coords);
|
|
+ }
|
|
// Paper end
|
|
|
|
@Nullable
|
|
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
|
|
if (!completablefuture.isDone()) { // Paper
|
|
// Paper start - async chunk io/loading
|
|
+ ChunkCoordIntPair pair = new ChunkCoordIntPair(x, z);
|
|
+ this.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
|
|
@@ -0,0 +0,0 @@ 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.clearPriorityTickets(pair); // Paper
|
|
} // Paper
|
|
ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
|
return ichunkaccess1;
|
|
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
if (flag && !currentlyUnloading) {
|
|
// CraftBukkit end
|
|
this.chunkMapDistance.a(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
|
|
+ if (isUrgent) this.markUrgent(chunkcoordintpair); // Paper
|
|
if (this.a(playerchunk, l)) {
|
|
GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler();
|
|
|
|
@@ -0,0 +0,0 @@ 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.clearPriorityTickets(chunkcoordintpair));
|
|
+ }
|
|
+ return future;
|
|
+ // Paper end
|
|
}
|
|
|
|
private boolean a(@Nullable PlayerChunk playerchunk, int i) {
|
|
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
@@ -0,0 +0,0 @@ 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() && this.ticksLived % 20 == 0) ((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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
|
+++ b/src/main/java/net/minecraft/server/MCUtil.java
|
|
@@ -0,0 +0,0 @@ public final class MCUtil {
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
private CompletableFuture<IChunkAccess> chunkSave;
|
|
public int oldTicketLevel;
|
|
private int ticketLevel;
|
|
- private int n;
|
|
+ private int n; public final int getCurrentPriority() { return n; } // Paper - OBFHELPER
|
|
final ChunkCoordIntPair location; // Paper - private -> package
|
|
private final short[] dirtyBlocks;
|
|
private int dirtyCount;
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
return null;
|
|
}
|
|
// Paper end - no-tick view distance
|
|
+ // Paper start - Chunk gen/load priority system
|
|
+ volatile int neighborPriority = -1;
|
|
+ final java.util.concurrent.ConcurrentHashMap<PlayerChunk, Integer> neighbors = new java.util.concurrent.ConcurrentHashMap<>();
|
|
+ final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<Integer> neighborPriorities = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
+
|
|
+ public int getPreferredPriority() {
|
|
+ int priority = neighborPriority; // if we have a neighbor priority, use it
|
|
+ int priorityBoost = chunkMap.chunkDistanceManager.getChunkPriority(location);
|
|
+ int basePriority = ticketLevel - priorityBoost;
|
|
+
|
|
+ if (priority == -1 || priority > basePriority) {
|
|
+ if (priorityBoost > 0) {
|
|
+ //System.out.println(location + " boost " + (basePriority) + " = " + ticketLevel + " - " + priorityBoost);
|
|
+ }
|
|
+ priority = basePriority;
|
|
+ if (ticketLevel >= 34 && priorityBoost == 0) {
|
|
+ priority += 5;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ return Math.max(1, Math.min(PlayerChunkMap.GOLDEN_TICKET, priority));
|
|
+ }
|
|
+ public void onNeighborRequest(PlayerChunk neighbor, ChunkStatus status) {
|
|
+ int currentPriority = getCurrentPriority();
|
|
+ if (!neighborPriorities.containsKey(neighbor.location.pair()) && (neighbor.neighborPriority == -1 || neighbor.neighborPriority > currentPriority)) {
|
|
+ this.neighbors.put(neighbor, currentPriority);
|
|
+ neighbor.setNeighborPriority(this, Math.max(1, currentPriority));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void setNeighborPriority(PlayerChunk requester, int priority) {
|
|
+ if (priority < neighborPriority || neighborPriority == -1) {
|
|
+ synchronized (neighborPriorities) {
|
|
+ if (priority < neighborPriority || neighborPriority == -1) {
|
|
+ neighborPriority = priority;
|
|
+ neighborPriorities.put(requester.location.pair(), Integer.valueOf(priority));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void onNeighborsDone() {
|
|
+ java.util.List<PlayerChunk> neighbors = new java.util.ArrayList<>(this.neighbors.keySet());
|
|
+ this.neighbors.clear();
|
|
+ for (PlayerChunk neighbor : neighbors) {
|
|
+ synchronized (neighbor.neighborPriorities) {
|
|
+ neighbor.neighborPriorities.remove(location.pair());
|
|
+ neighbor.recalcNeighborPriority();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void recalcNeighborPriority() {
|
|
+ neighborPriority = -1;
|
|
+ if (!neighborPriorities.isEmpty()) {
|
|
+ synchronized (neighborPriorities) {
|
|
+ for (Integer neighbor : neighborPriorities.values()) {
|
|
+ if (neighbor < neighborPriority || neighborPriority == -1) {
|
|
+ neighborPriority = neighbor;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public final double getDistanceFromPointInFront(EntityPlayer player, int dist) {
|
|
+ int inFront = dist * 16;
|
|
+ final float yaw = MCUtil.normalizeYaw(player.yaw);
|
|
+ double rads = Math.toRadians(yaw);
|
|
+ final double x = player.locX() + inFront * Math.cos(rads);
|
|
+ final double z = player.locZ() + inFront * Math.sin(rads);
|
|
+ return getDistance(x, z);
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+ // 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());
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
}
|
|
return null;
|
|
}
|
|
+ public static ChunkStatus getNextStatus(ChunkStatus status) {
|
|
+ if (status == ChunkStatus.FULL) {
|
|
+ return status;
|
|
+ }
|
|
+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1);
|
|
+ }
|
|
// Paper end
|
|
|
|
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
return this.n;
|
|
}
|
|
|
|
+ private void setPriority(int i) { d(i); } // Paper - OBFHELPER
|
|
private void d(int i) {
|
|
+ if (i == n) return; // Paper
|
|
this.n = i;
|
|
+ // Paper start
|
|
+ this.neighbors.keySet().forEach(neighbor -> {
|
|
+ if (neighbor.getCurrentPriority() > i) {
|
|
+ neighbor.setNeighborPriority(this, i);
|
|
+ this.w.changePriority(neighbor.location, neighbor::getCurrentPriority, neighbor.getCurrentPriority(), neighbor::setPriority);
|
|
+ }
|
|
+ });
|
|
+ // Paper end
|
|
}
|
|
|
|
public void a(int i) {
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
Chunk fullChunk = either.left().get();
|
|
PlayerChunk.this.isFullChunkReady = true;
|
|
fullChunk.playerChunk = PlayerChunk.this;
|
|
+ this.chunkMap.chunkDistanceManager.clearPriorityTickets(location);
|
|
|
|
|
|
}
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
|
|
}
|
|
|
|
- this.w.a(this.location, this::k, this.ticketLevel, this::d);
|
|
+ this.w.a(this.location, this::k, getPreferredPriority(), this::d); // Paper - preferred priority
|
|
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.
|
|
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -0,0 +0,0 @@ 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;
|
|
}
|
|
@@ -0,0 +0,0 @@ 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) -> 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,
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
});
|
|
// Paper end - no-tick view distance
|
|
}
|
|
+ // Paper start - Chunk Prioritization
|
|
+ private static final int[][] neighborMatrix = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
|
|
+ public void checkHighPriorityChunks(EntityPlayer player) {
|
|
+ MCUtil.getSpiralOutChunks(new BlockPosition(player), Math.min(7, getLoadViewDistance())).forEach(coord -> {
|
|
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
|
|
+ if (chunk == null || chunk.isFullChunkReady() || chunk.getTicketLevel() >= 34 ||
|
|
+ !world.getWorldBorder().isInBounds(coord)
|
|
+ ) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ double dist = chunk.getDistance(player);
|
|
+ // Prioritize immediate
|
|
+ if (dist <= 5) {
|
|
+ chunkDistanceManager.markHighPriority(coord, (int) (29 - dist));
|
|
+ return;
|
|
+ }
|
|
+ boolean hasNeighbor = false;
|
|
+ for (int[] matrix : neighborMatrix) {
|
|
+ long neighborKey = MCUtil.getCoordinateKey(coord.x + matrix[0], coord.x + matrix[1]);
|
|
+ PlayerChunk neighbor = getUpdatingChunk(neighborKey);
|
|
+ if (neighbor != null && neighbor.isFullChunkReady()) {
|
|
+ hasNeighbor = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!hasNeighbor) {
|
|
+ return;
|
|
+ }
|
|
+ // Prioritize Frustum near
|
|
+ double distFront1 = chunk.getDistanceFromPointInFront(player, 2);
|
|
+ if (distFront1 <= (4*4)) {
|
|
+ if (distFront1 <= (2 * 2)) {
|
|
+ chunkDistanceManager.markHighPriority(coord, 24);
|
|
+ } else {
|
|
+ chunkDistanceManager.markHighPriority(coord, 22);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Prioritize Frustum far
|
|
+ double distFront2 = chunk.getDistanceFromPointInFront(player, 4);
|
|
+ if (distFront2 <= (3*3)) {
|
|
+ if (distFront2 <= (2 * 2)) {
|
|
+ chunkDistanceManager.markHighPriority(coord, 23);
|
|
+ } else {
|
|
+ chunkDistanceManager.markHighPriority(coord, 20);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Prioritize nearby chunks
|
|
+ if (dist <= (5*5)) {
|
|
+ chunkDistanceManager.markHighPriority(coord, (int) (16 - Math.sqrt(dist*(4D/5D))));
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ // Paper end
|
|
|
|
public void updatePlayerMobTypeMap(Entity entity) {
|
|
if (!this.world.paperConfig.perPlayerMobSpawns) {
|
|
@@ -0,0 +0,0 @@ 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) {
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
|
|
ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1);
|
|
+ if (requestingNeighbor != null && requestingNeighbor != playerchunk) requestingNeighbor.onNeighborRequest(playerchunk, chunkstatus); // Paper
|
|
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = playerchunk.a(chunkstatus, this);
|
|
|
|
list.add(completablefuture);
|
|
@@ -0,0 +0,0 @@ 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
|
|
}
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
|
|
});
|
|
}, (runnable) -> {
|
|
+ playerchunk.onNeighborsDone(); // Paper
|
|
this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error
|
|
});
|
|
}
|
|
@@ -0,0 +0,0 @@ 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, playerchunk::getCurrentPriority)); // CraftBukkit - decompile error // Paper - use priority not ticket level....
|
|
});
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/TicketType.java
|
|
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
|
@@ -0,0 +0,0 @@ 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<Integer> PRIORITY = a("priority", Integer::compareTo, 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -0,0 +0,0 @@ public class CraftWorld implements World {
|
|
}
|
|
}
|
|
|
|
- return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
|
|
+ CompletableFuture<Chunk> future = 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());
|
|
}, MinecraftServer.getServer());
|
|
+ if (urgent) {
|
|
+ world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
|
|
+ }
|
|
+ return future;
|
|
+
|
|
}
|
|
// Paper end
|
|
|