525 lines
27 KiB
Diff
525 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 716d4eab382244ee9bc4855bf0f026e65ec057d6..20052dcbc71899165ac99801fa9f7753672ba997 100644
|
||
|
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
||
|
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
||
|
@@ -143,7 +143,7 @@ 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);
|
||
|
}
|
||
|
|
||
|
@@ -171,6 +171,48 @@ 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
|
||
|
@@ -386,7 +428,8 @@ 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 0a99b347d8497f097ef1da6560a5d0adc1374f25..d6f629d45e05167c22b6cd08a9709809a32b15a1 100644
|
||
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
||
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
||
|
@@ -431,6 +431,16 @@ 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
|
||
|
@@ -469,6 +479,8 @@ 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
|
||
|
@@ -477,6 +489,7 @@ 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;
|
||
|
@@ -529,6 +542,7 @@ 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();
|
||
|
|
||
|
@@ -541,8 +555,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.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 07a6fc3d88e7d44bfab7f3d6a0eef7dc132ab422..d60f659b368500e3a8c3305f99e60ffc643e2fbd 100644
|
||
|
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
|
||
|
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
|
||
|
@@ -441,6 +441,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() && 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 d129c7f54d9f65fff6f512d8ff5f1c3866632603..9b9536fba4a62c0153b921e678e6a9683bf2e37f 100644
|
||
|
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
||
|
+++ b/src/main/java/net/minecraft/server/MCUtil.java
|
||
|
@@ -658,6 +658,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 9e95c6b54f0855ddde6db6bd3e768e87fee6c21a..44721a4f446bdd5bf4575e4168a0570afd71c4d4 100644
|
||
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
||
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
||
|
@@ -25,7 +25,7 @@ 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;
|
||
|
@@ -68,6 +68,92 @@ 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());
|
||
|
@@ -166,6 +252,12 @@ 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) {
|
||
|
@@ -419,8 +511,18 @@ 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) {
|
||
|
@@ -508,6 +610,7 @@ public class PlayerChunk {
|
||
|
Chunk fullChunk = either.left().get();
|
||
|
PlayerChunk.this.isFullChunkReady = true;
|
||
|
fullChunk.playerChunk = PlayerChunk.this;
|
||
|
+ this.chunkMap.chunkDistanceManager.clearPriorityTickets(location);
|
||
|
|
||
|
|
||
|
}
|
||
|
@@ -583,7 +686,7 @@ 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.
|
||
|
@@ -670,6 +773,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 e772095e1c44842f743661a326c2a9a8a677ab02..5621416660d2722f26582fcecd5b61a164cd1530 100644
|
||
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||
|
@@ -352,6 +352,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;
|
||
|
}
|
||
|
@@ -370,7 +371,8 @@ 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,
|
||
|
@@ -387,6 +389,62 @@ 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) {
|
||
|
@@ -516,6 +574,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) {
|
||
|
@@ -533,6 +592,7 @@ 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);
|
||
|
@@ -997,14 +1057,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
|
||
|
}
|
||
|
@@ -1041,6 +1109,7 @@ 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
|
||
|
});
|
||
|
}
|
||
|
@@ -1133,7 +1202,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, 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 8055f5998213ab1c6c10d03d88d2b14d220a5e40..4913205c15a2b6d5ea058890b02090b494e9c177 100644
|
||
|
--- a/src/main/java/net/minecraft/server/TicketType.java
|
||
|
+++ b/src/main/java/net/minecraft/server/TicketType.java
|
||
|
@@ -23,6 +23,7 @@ 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 bbfbfeed12890c9d20d78a9661ab172c901f008c..589926d6029ca2a4aeb4f2c7903a5f9517deebef 100644
|
||
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||
|
@@ -2472,10 +2472,15 @@ 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
|
||
|
|