diff options
author | Aikar <[email protected]> | 2020-06-23 04:10:08 -0400 |
---|---|---|
committer | Aikar <[email protected]> | 2020-06-23 04:40:02 -0400 |
commit | c4ada0e1990030634f0ec3c5878ecc8e2050b30f (patch) | |
tree | 0a2ac5aca187f25da968d986611b98adc17a119e | |
parent | 37b244b50b55e2f8c8e928be516bb77f4d42f542 (diff) | |
download | Paper-c4ada0e1990030634f0ec3c5878ecc8e2050b30f.tar.gz Paper-c4ada0e1990030634f0ec3c5878ecc8e2050b30f.zip |
Fix many chunk loading issues
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
4 files changed, 193 insertions, 88 deletions
diff --git a/Spigot-Server-Patches/0528-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/Spigot-Server-Patches/0528-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch index 9078488a84..4ef1f2dc6f 100644 --- a/Spigot-Server-Patches/0528-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch +++ b/Spigot-Server-Patches/0528-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch @@ -90,7 +90,7 @@ index f617636a22167b06ac8073aa25efd8c7099155f0..0f40793f004639822b9d40521cd21ec5 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..f7be94e801e58a39e0efcc813d95bbd613afce03 100644 +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; @@ -101,6 +101,15 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 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 { } @@ -163,7 +172,7 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 return removed; // CraftBukkit } -@@ -182,6 +191,113 @@ public abstract class ChunkMapDistance { +@@ -182,6 +191,135 @@ public abstract class ChunkMapDistance { this.addTicketAtLevel(tickettype, chunkcoordintpair, i, t0); } @@ -181,8 +190,10 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + + 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, priority); ++ addPriorityTicket(coords, TicketType.PRIORITY, finalPriority); + }); + delayDistanceManagerTick = false; + chunkMap.world.getChunkProvider().tickDistanceManager(); @@ -197,17 +208,37 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + 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); -+ if (chunk != null && chunk.isFullChunkReady() && chunk.getTicketLevel() <= 33) { -+ return false; -+ } -+ if (chunk != null && chunk.getTicketLevel() > 33 && chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null) { ++ 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))) { @@ -277,51 +308,75 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 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 -@@ -384,24 +500,26 @@ public abstract class ChunkMapDistance { - Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkCoordIntPair(i)); // Paper - no-tick view distance +@@ -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) { -- ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error -- ChunkMapDistance.this.m.execute(() -> { ++ 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))) { -+ // Paper start - smarter ticket delay based on frustum and distance -+ scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> { -+ //ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error -+ if (this.c(this.c(i))) { // Copy c(c()) stuff below -+ // Paper end ++ 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 { ++ } ++ } else { // Paper ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error }, i, false)); } -- -- }); -- }, i, () -> { + + }); + }, i, () -> { - return j; -- })); -+ //}, i, () -> { -+ //return Math.min(PlayerChunkMap.GOLDEN_TICKET, (priority <= 6 ? 20 : 30) + priority); // Paper - delay new ticket adds to avoid spamming the queue -+ //})); // Paper ++ 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(new ChunkCoordIntPair(i)); // Paper ++ ChunkMapDistance.this.clearPriorityTickets(coords); // Paper }); }, i, true)); } -@@ -409,6 +527,99 @@ public abstract class ChunkMapDistance { +@@ -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()) || !this.c(this.c(i)) || getChunkPriority(chunkPos) > 0) { // Copied from above ++ if ((updatingChunk != null && updatingChunk.isFullChunkReady()) || !isChunkInRange(i) || getChunkPriority(chunkPos) > 0) { // Copied from above + // no longer needed + task.accept(1); + return; @@ -355,8 +410,7 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + + double dist = Math.min(frontDist, center); + if (!isFront) { -+ -+ ChunkCoordIntPair pointInBack = player.getChunkInFront(-5); ++ ChunkCoordIntPair pointInBack = player.getChunkInFront(-7); + pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4); + double backDist = MCUtil.distanceSq(pos, blockPos); + if (frontDist < backDist) { @@ -375,8 +429,8 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + } + if (minDist > 4) { + int desiredTimeDelayMax = isFront ? -+ (minDist < 10 ? 10 : 20) : // Front -+ (minDist < 10 ? 20 : 40); // Back ++ (minDist < 10 ? 7 : 15) : // Front ++ (minDist < 10 ? 15 : 45); // Back + desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32); + } + } else { @@ -389,7 +443,7 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && z == 0) continue; -+ long pair = new ChunkCoordIntPair(chunkPos.x + x, chunkPos.z + z).pair(); ++ 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)) { @@ -398,13 +452,14 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 + } + } + if (!hasAnyNeighbor) { -+ delay += 10; ++ delay += 20; + } + } + if (delay <= 0) { + task.accept((int) minDist); + } else { -+ MCUtil.scheduleTask((int) Math.min(delay, minDist >= 10 ? 40 : (minDist < 6 ? 5 : 20)), () -> scheduleChunkLoad(i, startTick, initialDistance, task), "Player Ticket Delayer"); ++ 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 @@ -412,6 +467,22 @@ index 7702fbefa598bce7e6a2d287f7ec36b78a62bff8..f7be94e801e58a39e0efcc813d95bbd6 @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 @@ -501,18 +572,23 @@ index 7a275bf3260f9fbefc41883c5ebdc1eb2196daf0..b6a7e475c6ebe499c641db5967adb167 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..4ed4ad6bc3b4ab6702ca500dc26e889dca6ed2d7 100644 +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,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -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 +133,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -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 } @@ -534,11 +610,11 @@ index 07a6fc3d88e7d44bfab7f3d6a0eef7dc132ab422..4ed4ad6bc3b4ab6702ca500dc26e889d // 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 +457,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -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()) ((WorldServer)world).getChunkProvider().playerChunkMap.checkHighPriorityChunks(this); // Paper ++ 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); @@ -555,7 +631,7 @@ index ce0bf608b71cf492fc31e89a360ecd83fa5c23a6..87d58002116f361d8255d79fc0dbd120 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..bc5e380aa37101382c359f1c9a25803e09cd238c 100644 +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 { @@ -698,7 +774,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e 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,15 @@ public class PlayerChunk { +@@ -165,6 +280,18 @@ public class PlayerChunk { } return null; } @@ -709,12 +785,15 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e + return CHUNK_STATUSES.get(status.getStatusIndex() + 1); + } + public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) { -+ return MCUtil.ensureMain(getStatusFutureUnchecked(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 +542,7 @@ public class PlayerChunk { +@@ -418,6 +545,7 @@ public class PlayerChunk { return this.n; } @@ -722,7 +801,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e private void d(int i) { this.n = i; } -@@ -436,7 +561,7 @@ public class PlayerChunk { +@@ -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)) { @@ -731,12 +810,12 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e Chunk chunk = (Chunk)either.left().orElse(null); if (chunk != null) { playerchunkmap.callbackExecutor.execute(() -> { -@@ -501,12 +626,13 @@ public class PlayerChunk { +@@ -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); MCUtil.ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main ++ 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(); @@ -746,25 +825,25 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e } -@@ -531,7 +657,7 @@ public class PlayerChunk { +@@ -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); MCUtil.ensureMain(this.tickingFuture).thenAccept((either) -> { // Paper - ensure main ++ 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 +688,7 @@ public class PlayerChunk { +@@ -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); MCUtil.ensureMain(this.entityTickingFuture).thenAccept((either) -> { // Paper ensureMain ++ 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 +707,29 @@ public class PlayerChunk { +@@ -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; } @@ -797,7 +876,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e Chunk chunk = (Chunk)either.left().orElse(null); if (chunk != null) { playerchunkmap.callbackExecutor.execute(() -> { -@@ -669,6 +811,7 @@ public class PlayerChunk { +@@ -669,6 +814,7 @@ public class PlayerChunk { public interface c { @@ -806,7 +885,7 @@ index aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44..bc5e380aa37101382c359f1c9a25803e } diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c 100644 +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; @@ -841,17 +920,20 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c if (newState.size() != 1) { return; } -@@ -393,7 +397,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -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) -> checkHighPriorityChunks(player)); ++ }, (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 +415,101 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -410,6 +418,116 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { }); // Paper end - no-tick view distance } @@ -878,33 +960,41 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c + 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) { ++ 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(); -+ chunkDistanceManager.delayDistanceManagerTick = true; + 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) { -+ chunkDistanceManager.markHighPriority(coord, (int) (27 - Math.sqrt(dist))); ++ updateChunkPriorityMap(priorities, coord.pair(), (int) (27 - Math.sqrt(dist))); + return; + } + + // Prioritize nearby chunks -+ chunkDistanceManager.markHighPriority(coord, (int) (16 - Math.sqrt(dist*(2D/3D)))); ++ updateChunkPriorityMap(priorities, coord.pair(), (int) (20 - Math.sqrt(dist) * twoThirdModifier)); + }); + + // Prioritize Frustum near 3 @@ -913,7 +1003,8 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c + MCUtil.getSpiralOutChunks(pos, Math.min(5, viewDistance)).forEach(coord -> { + if (shouldSkipPrioritization(coord)) return; + -+ chunkDistanceManager.markHighPriority(coord, 26); ++ 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 @@ -923,7 +1014,8 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c + MCUtil.getSpiralOutChunks(pos, 4).forEach(coord -> { + if (shouldSkipPrioritization(coord)) return; + -+ chunkDistanceManager.markHighPriority(coord, 20); ++ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); ++ updateChunkPriorityMap(priorities, coord.pair(), (int) (25 - Math.sqrt(dist) * twoThirdModifier)); + }); + } + @@ -935,13 +1027,18 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c + if (shouldSkipPrioritization(coord)) { + return; + } -+ chunkDistanceManager.markHighPriority(coord, 15); ++ 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) { @@ -953,7 +1050,7 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c public void updatePlayerMobTypeMap(Entity entity) { if (!this.world.paperConfig.perPlayerMobSpawns) { -@@ -539,6 +639,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -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; @@ -961,7 +1058,7 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c for (int l = -i; l <= i; ++l) { for (int i1 = -i; i1 <= i; ++i1) { -@@ -557,6 +658,14 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -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); @@ -976,7 +1073,7 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c list.add(completablefuture); } -@@ -1022,14 +1131,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1022,14 +1149,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { }; CompletableFuture<NBTTagCompound> chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z); @@ -1004,7 +1101,7 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c return ret; // Paper end } -@@ -1158,7 +1275,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1158,7 +1293,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { long i = playerchunk.i().pair(); playerchunk.getClass(); @@ -1014,19 +1111,19 @@ index 0aa14bfca6e1845eb6e9f5bd4e0e36335fa7f532..6c0dbad8e06b02b32dcff518cc2a5f7c } diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index d52fbda79fe1c52d3ddb53c0f1c1f521d7620702..7123e197c7ed01afd4fbf7aa0760611373039a13 100644 +index d52fbda79fe1c52d3ddb53c0f1c1f521d7620702..f9cb87a3be35575ecf3362b10dc7fe5ebadb56ec 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java -@@ -1277,6 +1277,7 @@ public class PlayerConnection implements PacketListenerPlayIn { - // CraftBukkit end - +@@ -1279,6 +1279,7 @@ public class PlayerConnection implements PacketListenerPlayIn { 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.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..b0585346bf5125bebc482246bbb91c4b08c55816 100644 +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 { @@ -1048,11 +1145,19 @@ index 6daca5c0ffd1d84f9a25cd106e8992a055dfb912..b0585346bf5125bebc482246bbb91c4b }; }); } -@@ -764,6 +763,7 @@ public abstract class PlayerList { +@@ -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 -+ worldserver.getChunkProvider().markAreaHighPriority(new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 28, 3); // Paper - load area at high priority ++ 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/Spigot-Server-Patches/0530-Optimize-sending-packets-to-nearby-locations-sounds-.patch b/Spigot-Server-Patches/0530-Optimize-sending-packets-to-nearby-locations-sounds-.patch index 5cd96e93a6..fe477cdf8b 100644 --- a/Spigot-Server-Patches/0530-Optimize-sending-packets-to-nearby-locations-sounds-.patch +++ b/Spigot-Server-Patches/0530-Optimize-sending-packets-to-nearby-locations-sounds-.patch @@ -11,10 +11,10 @@ This will drastically cut down on packet sending cost for worlds with lots of players in them. diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 6daca5c0ffd1d84f9a25cd106e8992a055dfb912..c03395ce824ec1305b3cabb63343922d32a02b85 100644 +index f133e7baf958b031819c2c72ccd21c52ba9a683d..ab8247eb790be2084be7544f72e5b6661151905d 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java -@@ -1030,11 +1030,30 @@ public abstract class PlayerList { +@@ -1031,11 +1031,30 @@ public abstract class PlayerList { world = (WorldServer) entityhuman.world; } @@ -51,7 +51,7 @@ index 6daca5c0ffd1d84f9a25cd106e8992a055dfb912..c03395ce824ec1305b3cabb63343922d // CraftBukkit start - Test if player receiving packet can see the source of the packet diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 9e32e2db10f5faaa3c5f4adc5cbc2c1a2e4d3073..854c97bddb711ed252c2ae8398247641fdfb10be 100644 +index aaf85a1497de98522e3a01d4f81a267c4b0cc087..2c9acffe5a62af43ff4f4ccdb6962929d645e226 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -70,6 +70,7 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0532-Improve-Chunk-Status-Transition-Speed.patch b/Spigot-Server-Patches/0532-Improve-Chunk-Status-Transition-Speed.patch index 1c5220364b..85c0553f68 100644 --- a/Spigot-Server-Patches/0532-Improve-Chunk-Status-Transition-Speed.patch +++ b/Spigot-Server-Patches/0532-Improve-Chunk-Status-Transition-Speed.patch @@ -36,7 +36,7 @@ scenario / path: Previously would have hopped to SERVER around 12+ times there extra. diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 04dcb79c6033f1dec62c5df49937a4ef067a2cb8..f8820f24075e7f42f67426fc9ecf5238f4499b72 100644 +index 52da9c78c0cc2b21533a1477a25a3dda492700e4..69899c100dd86c6c4795013364472336327ce036 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -56,6 +56,13 @@ public class PlayerChunk { @@ -54,7 +54,7 @@ index 04dcb79c6033f1dec62c5df49937a4ef067a2cb8..f8820f24075e7f42f67426fc9ecf5238 // Paper start - no-tick view distance public final Chunk getSendingChunk() { diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c..394cea57b3871c2e11d1f7e0e9546df64ff3bafe 100644 +index 3139d55bb801b6c433de7274be4a1d0c0fd1eef7..98adcb6390105a183d66975ed9659906b609ce08 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -88,6 +88,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -81,7 +81,7 @@ index 6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c..394cea57b3871c2e11d1f7e0e9546df6 ThreadedMailbox<Runnable> threadedmailbox = ThreadedMailbox.a(executor, "worldgen"); iasynctaskhandler.getClass(); -@@ -706,7 +716,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -724,7 +734,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return either.mapLeft((list) -> { return (Chunk) list.get(list.size() / 2); }); @@ -90,7 +90,7 @@ index 6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c..394cea57b3871c2e11d1f7e0e9546df6 } @Nullable -@@ -1056,7 +1066,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1074,7 +1084,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { IChunkAccess ichunkaccess = (IChunkAccess) optional.get(); if (ichunkaccess.getChunkStatus().b(chunkstatus)) { @@ -99,7 +99,7 @@ index 6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c..394cea57b3871c2e11d1f7e0e9546df6 if (chunkstatus == ChunkStatus.LIGHT) { completablefuture1 = this.b(playerchunk, chunkstatus); -@@ -1072,7 +1082,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1090,7 +1100,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return this.b(playerchunk, chunkstatus); } } @@ -108,7 +108,7 @@ index 6c0dbad8e06b02b32dcff518cc2a5f7c8c1c316c..394cea57b3871c2e11d1f7e0e9546df6 } } -@@ -1183,6 +1193,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1201,6 +1211,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return CompletableFuture.completedFuture(Either.right(playerchunk_failure)); }); }, (runnable) -> { diff --git a/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch b/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch index 3318fd1472..4b00c7d98b 100644 --- a/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch +++ b/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch @@ -1337,10 +1337,10 @@ index 8cedfdd820cc02a76607b53e0b054fc74654f907..a9795394c9b17f9f0ce4c4f9c8f51a48 private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072); private static final int maxPoolSize = Integer.getInteger("Paper.maxNibblePoolSize", (int) Math.min(6, Math.max(1, Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024)) * (nibbleBucketSizeMultiplier * 8)); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index d04c8cdcc16ea7f0a49dc5720ddc8e47be0b634c..9c1be2e2d42701a96e8c40b377d13f5c0e92bf06 100644 +index 69899c100dd86c6c4795013364472336327ce036..2edb4904d3071aa9de6517c375410b814a45cfbe 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -725,6 +725,7 @@ public class PlayerChunk { +@@ -728,6 +728,7 @@ public class PlayerChunk { ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; } chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority); @@ -1349,10 +1349,10 @@ index d04c8cdcc16ea7f0a49dc5720ddc8e47be0b634c..9c1be2e2d42701a96e8c40b377d13f5c if (getCurrentPriority() != priority) { this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 394cea57b3871c2e11d1f7e0e9546df64ff3bafe..8abf276a325cbc3a863fb89276526790c4de2692 100644 +index 98adcb6390105a183d66975ed9659906b609ce08..201221df63d4ec8e704fee9126240891f2b1c37d 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -629,6 +629,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -647,6 +647,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { // Paper end } |