Improve Chunk Priority, Frustum Priority and Load Speed Algorithms

Fix bug where mojang has a -90 modifier in yaw resulting in us calculating
chunks to the players left rather than in front of them.

Drastically improve Frustum Prioritization function to reduce lag from its
calculations (Found it was being spammed really heavy on world add/teleport)

Also improved the logic behind choosing chunks to prioritize.

Add Priority tickets to a radius of 3 on any login, world chnge or teleport

This should help improve world load / chunk sending upon a player changing
locations by loading those chunks faster.

Improved the Player Ticket Delayer to be a little bit smarter about delays to
let closer chunks load a bit faster and only delay the farther out ones more.

This update will provide significant improvements to priority of chunks and
reduce the cpu cost of doing these calculations.

Fixes #3530
This commit is contained in:
Aikar
2020-06-09 23:06:34 -04:00
parent bfe5d554d3
commit 2a50b14734

View File

@@ -170,6 +170,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start + // Paper start
+ public static final int PRIORITY_TICKET_LEVEL = PlayerChunkMap.GOLDEN_TICKET; + public static final int PRIORITY_TICKET_LEVEL = PlayerChunkMap.GOLDEN_TICKET;
+ public static final int URGENT_PRIORITY = 29; + public static final int URGENT_PRIORITY = 29;
+ public boolean delayDistanceManagerTick = false;
+ public boolean markUrgent(ChunkCoordIntPair coords) { + public boolean markUrgent(ChunkCoordIntPair coords) {
+ return addPriorityTicket(coords, TicketType.URGENT, URGENT_PRIORITY); + return addPriorityTicket(coords, TicketType.URGENT, URGENT_PRIORITY);
+ } + }
@@ -178,10 +179,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return addPriorityTicket(coords, TicketType.PRIORITY, priority); + return addPriorityTicket(coords, TicketType.PRIORITY, priority);
+ } + }
+ +
+ public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) {
+ delayDistanceManagerTick = true;
+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> {
+ addPriorityTicket(coords, TicketType.PRIORITY, priority);
+ });
+ 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 addPriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> ticketType, int priority) { + private boolean addPriorityTicket(ChunkCoordIntPair coords, TicketType<ChunkCoordIntPair> ticketType, int priority) {
+ AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket"); + AsyncCatcher.catchOp("ChunkMapDistance::addPriorityTicket");
+ long pair = coords.pair(); + long pair = coords.pair();
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(pair); + PlayerChunk chunk = chunkMap.getUpdatingChunk(pair);
+ if (chunk != null && chunk.isFullChunkReady()) {
+ return false;
+ }
+ if (getChunkPriority(coords) >= priority) {
+ return false;
+ }
+ +
+ boolean success; + boolean success;
+ if (!(success = updatePriorityTicket(coords, ticketType, priority))) { + if (!(success = updatePriorityTicket(coords, ticketType, priority))) {
@@ -189,11 +214,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ ticket.priority = priority; + ticket.priority = priority;
+ success = this.addTicket(pair, ticket); + success = this.addTicket(pair, ticket);
+ } else { + } else {
+ if (updatingChunk == null) { + if (chunk == null) {
+ updatingChunk = chunkMap.getUpdatingChunk(pair); + chunk = chunkMap.getUpdatingChunk(pair);
+ } + }
+ chunkMap.queueHolderUpdate(updatingChunk); + 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(); + chunkMap.world.getChunkProvider().tickDistanceManager();
+ +
+ return success; + return success;
@@ -256,7 +284,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- ChunkMapDistance.this.m.execute(() -> { - ChunkMapDistance.this.m.execute(() -> {
- if (this.c(this.c(i))) { - if (this.c(this.c(i))) {
+ // Paper start - smarter ticket delay based on frustum and distance + // Paper start - smarter ticket delay based on frustum and distance
+ scheduleChunkLoad(i, MinecraftServer.currentTick, (priority) -> { + scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> {
+ ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error + ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
+ if (chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i) != null && this.c(this.c(i))) { // Copy c(c()) stuff below + if (chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i) != null && this.c(this.c(i))) { // Copy c(c()) stuff below
+ // Paper end + // Paper end
@@ -271,8 +299,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}, i, () -> { }, i, () -> {
- return j; - return j;
- })); - }));
+ return priority; // Paper + return Math.min(PlayerChunkMap.GOLDEN_TICKET, (priority <= 6 ? 20 : 30) + priority); // Paper - delay new ticket adds to avoid spamming the queue
+ })); }); + })); }); // Paper
} else { } else {
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
ChunkMapDistance.this.m.execute(() -> { ChunkMapDistance.this.m.execute(() -> {
@@ -286,12 +314,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
+ // Paper start - smart scheduling of player tickets + // Paper start - smart scheduling of player tickets
+ public void scheduleChunkLoad(long i, long startTick, java.util.function.Consumer<Integer> task) { + public void scheduleChunkLoad(long i, long startTick, int initialDistance, java.util.function.Consumer<Integer> task) {
+ long elapsed = MinecraftServer.currentTick - startTick; + long elapsed = MinecraftServer.currentTick - startTick;
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(i); + PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(i);
+ if ((updatingChunk != null && updatingChunk.isFullChunkReady()) || !this.c(this.c(i))) { // Copied from above + if ((updatingChunk != null && updatingChunk.isFullChunkReady()) || !this.c(this.c(i))) { // Copied from above
+ // no longer needed + // no longer needed
+ task.accept(1); + task.accept(initialDistance);
+ return; + return;
+ } + }
+ +
@@ -299,26 +327,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ double minDist = Double.MAX_VALUE; + double minDist = Double.MAX_VALUE;
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i); + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(i);
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i); + ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i);
+ if (players != null) { + if (elapsed == 0 && initialDistance <= 4) {
+ BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire(); + // 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(); + Object[] backingSet = players.getBackingSet();
+ +
+ BlockPosition blockPos = chunkPos.asPosition(); + BlockPosition blockPos = chunkPos.asPosition();
+ +
+ boolean isFront = false; + boolean isFront = false;
+ BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire();
+ for (int index = 0, len = backingSet.length; index < len; ++index) { + for (int index = 0, len = backingSet.length; index < len; ++index) {
+ if (!(backingSet[index] instanceof EntityPlayer)) { + if (!(backingSet[index] instanceof EntityPlayer)) {
+ continue; + continue;
+ } + }
+ EntityPlayer player = (EntityPlayer) backingSet[index]; + EntityPlayer player = (EntityPlayer) backingSet[index];
+ BlockPosition pointInFront = player.getPointInFront(3 * 16).add(0, (int) -player.locY(), 0); +
+ pos.setValues(((int) player.locX() >> 4) << 4, 0, ((int) player.locZ() >> 4) << 4); + ChunkCoordIntPair pointInFront = player.getChunkInFront(5);
+ double frontDist = MCUtil.distanceSq(pointInFront, blockPos); + 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 center = MCUtil.distanceSq(pos, blockPos);
+
+ double dist = Math.min(frontDist, center); + double dist = Math.min(frontDist, center);
+ if (!isFront) { + if (!isFront) {
+ BlockPosition pointInBack = player.getPointInFront(3 * 16 * -1).add(0, (int) -player.locY(), 0); +
+ double backDist = MCUtil.distanceSq(pointInBack, blockPos); + ChunkCoordIntPair pointInBack = player.getChunkInFront(-5);
+ pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4);
+ double backDist = MCUtil.distanceSq(pos, blockPos);
+ if (frontDist < backDist) { + if (frontDist < backDist) {
+ isFront = true; + isFront = true;
+ } + }
@@ -328,13 +365,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ pos.close(); + pos.close();
+ if (minDist < Double.MAX_VALUE) { + if (minDist == Double.MAX_VALUE) {
+ minDist = 15;
+ } else {
+ minDist = Math.sqrt(minDist) / 16; + minDist = Math.sqrt(minDist) / 16;
+ if (minDist > 5) { + }
+ desireDelay += ((isFront ? 15 : 30) * 20) * (minDist / 32); + if (minDist > 4) {
+ } + int desiredTimeDelayMax = isFront ?
+ (minDist < 10 ? 10 : 15) : // Front
+ (minDist < 10 ? 15 : 30); // Back
+ desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32);
+ } + }
+ } else { + } else {
+ minDist = initialDistance;
+ desireDelay = 1; + desireDelay = 1;
+ } + }
+ long delay = desireDelay - elapsed; + long delay = desireDelay - elapsed;
@@ -345,7 +388,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (x == 0 && z == 0) continue; + if (x == 0 && z == 0) continue;
+ long pair = new ChunkCoordIntPair(chunkPos.x + x, chunkPos.z + z).pair(); + long pair = new ChunkCoordIntPair(chunkPos.x + x, chunkPos.z + z).pair();
+ PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair); + PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair);
+ if (neighbor != null && neighbor.isFullChunkReady()) { + ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null;
+ if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) {
+ hasAnyNeighbor = true; + hasAnyNeighbor = true;
+ } + }
+ } + }
@@ -355,9 +399,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ if (delay <= 0) { + if (delay <= 0) {
+ task.accept(Math.min(PlayerChunkMap.GOLDEN_TICKET, minDist < Double.MAX_VALUE ? (int) minDist : 15)); + task.accept((int) minDist);
+ } else { + } else {
+ MCUtil.scheduleTask((int) Math.min(delay, 20), () -> scheduleChunkLoad(i, startTick, task), "Player Ticket Delayer"); + MCUtil.scheduleTask((int) Math.min(delay, minDist >= 8 ? 60 : 20), () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer");
+ } + }
+ } + }
+ // Paper end + // Paper end
@@ -382,6 +426,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return this.chunkMapDistance.markHighPriority(coords, 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) { + public void clearPriorityTickets(ChunkCoordIntPair coords) {
+ this.chunkMapDistance.clearPriorityTickets(coords); + this.chunkMapDistance.clearPriorityTickets(coords);
+ } + }
@@ -440,6 +492,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- private boolean tickDistanceManager() { - private boolean tickDistanceManager() {
+ public boolean tickDistanceManager() { // Paper - public + public boolean tickDistanceManager() { // Paper - public
+ if (chunkMapDistance.delayDistanceManagerTick) return false; // Paper
boolean flag = this.chunkMapDistance.a(this.playerChunkMap); boolean flag = this.chunkMapDistance.a(this.playerChunkMap);
boolean flag1 = this.playerChunkMap.b(); boolean flag1 = this.playerChunkMap.b();
@@ -447,18 +500,32 @@ diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/jav
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java --- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/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 {
private int lastArmorScored = Integer.MIN_VALUE;
private int lastExpLevelScored = Integer.MIN_VALUE;
private int lastExpTotalScored = Integer.MIN_VALUE;
+ public long lastHighPriorityChecked; // Paper
private float lastHealthSent = -1.0E8F;
private int lastFoodSent = -99999999;
private boolean lastSentSaturationZero = true;
@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.maxHealthCache = this.getMaxHealth(); this.maxHealthCache = this.getMaxHealth();
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
} }
+ // Paper start + // Paper start
+ public BlockPosition getPointInFront(double inFront) { + public BlockPosition getPointInFront(double inFront) {
+ final float yaw = MCUtil.normalizeYaw(this.yaw); + double rads = Math.toRadians(MCUtil.normalizeYaw(this.yaw+90)); // MC rotates yaw 90 for some odd reason
+ double rads = Math.toRadians(yaw);
+ final double x = locX() + inFront * Math.cos(rads); + final double x = locX() + inFront * Math.cos(rads);
+ final double z = locZ() + inFront * Math.sin(rads); + final double z = locZ() + inFront * Math.sin(rads);
+ return new BlockPosition(x, locY(), z); + 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 + // Paper end
// Yes, this doesn't match Vanilla, but it's the best we can do for now. // Yes, this doesn't match Vanilla, but it's the best we can do for now.
@@ -467,7 +534,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
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) 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(); super.tick();
} }
+ if (valid && isAlive() && this.ticksLived % 20 == 0) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper + if (valid && isAlive()) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper
for (int i = 0; i < this.inventory.getSize(); ++i) { for (int i = 0; i < this.inventory.getSize(); ++i) {
ItemStack itemstack = this.inventory.getItem(i); ItemStack itemstack = this.inventory.getItem(i);
@@ -812,76 +879,75 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ +
+ public void checkHighPriorityChunks(EntityPlayer player) { + public void checkHighPriorityChunks(EntityPlayer player) {
+ BlockPosition front2 = player.getPointInFront(16*2); + int currentTick = MinecraftServer.currentTick;
+ BlockPosition front4 = player.getPointInFront(16*4); + if (currentTick - player.lastHighPriorityChecked < 20) {
+ BlockPosition front6 = player.getPointInFront(16*6); + return;
+ int viewDistance = getLoadViewDistance();
+ int maxDistSq = (viewDistance * 16) * (viewDistance * 16);
+
+ // Prioritize Frustum near 2
+ int dist3Sq = 3 * 3;
+ MCUtil.getSpiralOutChunks(front2, Math.min(5, viewDistance)).forEach(coord -> {
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
+ if (shouldSkipPrioritization(coord, chunk, player, maxDistSq)) return;
+
+ double dist = chunk.getDistanceFrom(front2);
+ if (dist <= dist3Sq) {
+ chunkDistanceManager.markHighPriority(coord, 26);
+ } else {
+ chunkDistanceManager.markHighPriority(coord, 24);
+ }
+ });
+ // Prioritize Frustum near 4
+ if (viewDistance > 4) {
+ MCUtil.getSpiralOutChunks(front4, Math.min(4, viewDistance)).forEach(coord -> {
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
+ if (shouldSkipPrioritization(coord, chunk, player, maxDistSq)) return;
+
+ double dist = chunk.getDistanceFrom(front4);
+ if (dist <= dist3Sq) {
+ chunkDistanceManager.markHighPriority(coord, 22);
+ } else {
+ chunkDistanceManager.markHighPriority(coord, 20);
+ }
+
+ });
+ } + }
+ player.lastHighPriorityChecked = currentTick;
+ +
+ // Prioritize Frustum far 6 + int viewDistance = getEffectiveNoTickViewDistance();
+ if (viewDistance > 6) { + chunkDistanceManager.delayDistanceManagerTick = true;
+ MCUtil.getSpiralOutChunks(front6, Math.min(4, viewDistance)).forEach(coord -> { + BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire();
+ PlayerChunk chunk = getUpdatingChunk(coord.pair());
+ if (shouldSkipPrioritization(coord, chunk, player, maxDistSq)) return;
+
+ double dist = chunk.getDistanceFrom(front6);
+ if (dist <= dist3Sq) {
+ chunkDistanceManager.markHighPriority(coord, 15);
+ }
+ });
+ }
+ +
+ // Prioritize circular near + // Prioritize circular near
+ MCUtil.getSpiralOutChunks(new BlockPosition(player), Math.min(5, viewDistance)).forEach(coord -> { + double playerChunkX = MathHelper.floor(player.locX()) >> 4;
+ PlayerChunk chunk = getUpdatingChunk(coord.pair()); + double playerChunkZ = MathHelper.floor(player.locZ()) >> 4;
+ if (shouldSkipPrioritization(coord, chunk, player, maxDistSq)) return; + pos.setValues(player.locX(), 0, player.locZ());
+ double dist = chunk.getDistance(player); + 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 + // Prioritize immediate
+ if (dist <= dist3Sq) { + if (dist <= 4 * 4) {
+ chunkDistanceManager.markHighPriority(coord, (int) (27 - dist)); + chunkDistanceManager.markHighPriority(coord, (int) (27 - Math.sqrt(dist)));
+ return; + return;
+ } + }
+ +
+ // Prioritize nearby chunks + // Prioritize nearby chunks
+ if (dist <= (5*5)) { + chunkDistanceManager.markHighPriority(coord, (int) (16 - Math.sqrt(dist*(2D/3D))));
+ chunkDistanceManager.markHighPriority(coord, (int) (16 - Math.sqrt(dist*(4D/5D))));
+ }
+ }); + });
+
+ // 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;
+
+ chunkDistanceManager.markHighPriority(coord, 26);
+ });
+
+ // 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;
+
+ chunkDistanceManager.markHighPriority(coord, 20);
+ });
+ }
+
+ // 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;
+ }
+ chunkDistanceManager.markHighPriority(coord, 15);
+ });
+ }
+
+ pos.close();
+ chunkDistanceManager.delayDistanceManagerTick = false;
+ world.getChunkProvider().tickDistanceManager();
+ } + }
+ +
+ private boolean shouldSkipPrioritization(ChunkCoordIntPair coord, PlayerChunk chunk, EntityPlayer player, int viewDistance) { + private boolean shouldSkipPrioritization(ChunkCoordIntPair coord) {
+ return chunk == null || chunk.isFullChunkReady() || !world.getWorldBorder().isInBounds(coord) + if (playerViewDistanceNoTickMap.getObjectsInRange(coord.pair()) == null) return true;
+ || isUnloading(chunk) || chunk.getDistance(player) > viewDistance; + PlayerChunk chunk = getUpdatingChunk(coord.pair());
+ return chunk != null && (chunk.isFullChunkReady());
+ } + }
+ // Paper end + // Paper end
@@ -947,6 +1013,49 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}); });
} }
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn {
// CraftBukkit end
this.A = this.e;
+ this.player.getWorldServer().getChunkProvider().markAreaHighPriority(new ChunkCoordIntPair(MathHelper.floor(d1) >> 4, MathHelper.floor(d3) >> 4), 28, 3); // Paper - load area high priority
this.player.setLocation(d0, d1, d2, f, f1);
this.syncPosition(); // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ public abstract class PlayerList {
entityplayer, finalWorldserver, networkmanager, playerconnection,
nbttagcompound, networkmanager.getSocketAddress().toString(), lastKnownName
);
- //playerChunkMap.chunkDistanceManager.removeTicketAtLevel(TicketType.LOGIN, pos, 31, pos.pair());
};
});
}
@@ -0,0 +0,0 @@ public abstract class PlayerList {
// CraftBukkit end
worldserver.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
+ worldserver.getChunkProvider().markAreaHighPriority(new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 28, 3); // Paper - load area at high priority
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 diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/Ticket.java --- a/src/main/java/net/minecraft/server/Ticket.java
@@ -987,3 +1096,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null); net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null);
return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -0,0 +0,0 @@ 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");