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
This commit is contained in:
Aikar 2020-06-23 04:10:08 -04:00
commit 1c5229f4e4

View file

@ -101,6 +101,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public abstract class ChunkMapDistance { public abstract class ChunkMapDistance {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance { @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
} }
@ -181,8 +190,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) { + public void markAreaHighPriority(ChunkCoordIntPair center, int priority, int radius) {
+ delayDistanceManagerTick = true; + delayDistanceManagerTick = true;
+ priority = Math.min(URGENT_PRIORITY - 1, Math.max(1, priority));
+ int finalPriority = priority;
+ MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> { + MCUtil.getSpiralOutChunks(center.asPosition(), radius).forEach(coords -> {
+ addPriorityTicket(coords, TicketType.PRIORITY, priority); + addPriorityTicket(coords, TicketType.PRIORITY, finalPriority);
+ }); + });
+ delayDistanceManagerTick = false; + delayDistanceManagerTick = false;
+ chunkMap.world.getChunkProvider().tickDistanceManager(); + chunkMap.world.getChunkProvider().tickDistanceManager();
@ -197,17 +208,37 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ chunkMap.world.getChunkProvider().tickDistanceManager(); + 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) { + 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 chunk = chunkMap.getUpdatingChunk(pair); + PlayerChunk chunk = chunkMap.getUpdatingChunk(pair);
+ if (chunk != null && chunk.isFullChunkReady() && chunk.getTicketLevel() <= 33) { + boolean needsTicket = chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null && !hasPlayerTicket(coords, 33);
+ return false; +
+ } + if (needsTicket) {
+ if (chunk != null && chunk.getTicketLevel() > 33 && chunkMap.playerViewDistanceNoTickMap.getObjectsInRange(pair) != null) {
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords); + Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, coords);
+ getOnPlayerTicketAddQueue().add(pair);
+ addTicket(pair, ticket); + addTicket(pair, ticket);
+ } + }
+ if ((chunk != null && chunk.isFullChunkReady())) {
+ if (needsTicket) {
+ chunkMap.world.getChunkProvider().tickDistanceManager();
+ }
+ return needsTicket;
+ }
+ +
+ boolean success; + boolean success;
+ if (!(success = updatePriorityTicket(coords, ticketType, priority))) { + if (!(success = updatePriorityTicket(coords, ticketType, priority))) {
@ -278,37 +309,58 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier)); return this.addTicket(chunkcoordintpair.pair(), new Ticket<>(ticketType, level, identifier));
// CraftBukkit end // CraftBukkit end
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance { @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkCoordIntPair(i)); // Paper - no-tick view distance
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) { if (flag1) {
- ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error + scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> { // Paper - smarter ticket delay based on frustum and distance
- ChunkMapDistance.this.m.execute(() -> { + // Paper start - recheck its still valid if not cancel
- if (this.c(this.c(i))) { + if (!isChunkInRange(i)) {
+ // Paper start - smarter ticket delay based on frustum and distance + ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> {
+ scheduleChunkLoad(i, MinecraftServer.currentTick, j, (priority) -> { + ChunkMapDistance.this.m.execute(() -> {
+ //ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error + ChunkMapDistance.this.removeTicket(i, ticket);
+ if (this.c(this.c(i))) { // Copy c(c()) stuff below + 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 + // Paper end
ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
ChunkMapDistance.this.m.execute(() -> {
- if (this.c(this.c(i))) {
+ if (isChunkInRange(i)) { if (!hasPlayerTicket(coords, 33)) { // Paper - high priority might of already added it
ChunkMapDistance.this.addTicket(i, ticket); ChunkMapDistance.this.addTicket(i, ticket);
ChunkMapDistance.this.l.add(i); ChunkMapDistance.this.l.add(i);
} else { - } else {
+ }
+ } else { // Paper
ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error ChunkMapDistance.this.k.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
}, i, false)); }, i, false));
} }
-
- }); });
- }, i, () -> { }, i, () -> {
- return j; - return j;
- })); + return Math.min(PlayerChunkMap.GOLDEN_TICKET, priority); // Paper
+ //}, i, () -> { }));
+ //return Math.min(PlayerChunkMap.GOLDEN_TICKET, (priority <= 6 ? 20 : 30) + priority); // Paper - delay new ticket adds to avoid spamming the queue
+ //})); // Paper
+ }); // Paper + }); // 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(() -> {
ChunkMapDistance.this.removeTicket(i, ticket); ChunkMapDistance.this.removeTicket(i, ticket);
+ ChunkMapDistance.this.clearPriorityTickets(new ChunkCoordIntPair(i)); // Paper + ChunkMapDistance.this.clearPriorityTickets(coords); // Paper
}); });
}, i, true)); }, i, true));
} }
@ -317,11 +369,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
+ // Paper start - smart scheduling of player tickets + // 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) { + 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;
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i); + ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(i);
+ PlayerChunk updatingChunk = chunkMap.getUpdatingChunk(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 + // no longer needed
+ task.accept(1); + task.accept(1);
+ return; + return;
@ -355,8 +410,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ double dist = Math.min(frontDist, center); + double dist = Math.min(frontDist, center);
+ if (!isFront) { + if (!isFront) {
+ + ChunkCoordIntPair pointInBack = player.getChunkInFront(-7);
+ ChunkCoordIntPair pointInBack = player.getChunkInFront(-5);
+ pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4); + pos.setValues(pointInBack.x << 4, 0, pointInBack.z << 4);
+ double backDist = MCUtil.distanceSq(pos, blockPos); + double backDist = MCUtil.distanceSq(pos, blockPos);
+ if (frontDist < backDist) { + if (frontDist < backDist) {
@ -375,8 +429,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ if (minDist > 4) { + if (minDist > 4) {
+ int desiredTimeDelayMax = isFront ? + int desiredTimeDelayMax = isFront ?
+ (minDist < 10 ? 10 : 20) : // Front + (minDist < 10 ? 7 : 15) : // Front
+ (minDist < 10 ? 20 : 40); // Back + (minDist < 10 ? 15 : 45); // Back
+ desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32); + desireDelay += (desiredTimeDelayMax * 20) * (minDist / 32);
+ } + }
+ } else { + } else {
@ -389,7 +443,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ for (int x = -1; x <= 1; x++) { + for (int x = -1; x <= 1; x++) {
+ for (int z = -1; z <= 1; z++) { + for (int z = -1; z <= 1; z++) {
+ 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 = ChunkCoordIntPair.pair(chunkPos.x + x, chunkPos.z + z);
+ PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair); + PlayerChunk neighbor = chunkMap.getUpdatingChunk(pair);
+ ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null; + ChunkStatus current = neighbor != null ? neighbor.getChunkHolderStatus() : null;
+ if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) { + if (current != null && current.isAtLeastStatus(ChunkStatus.LIGHT)) {
@ -398,13 +452,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ if (!hasAnyNeighbor) { + if (!hasAnyNeighbor) {
+ delay += 10; + delay += 20;
+ } + }
+ } + }
+ if (delay <= 0) { + if (delay <= 0) {
+ task.accept((int) minDist); + task.accept((int) minDist);
+ } else { + } 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 + // Paper end
@ -412,6 +467,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@Override @Override
public void a() { public void a() {
super.a(); super.a();
@@ -0,0 +0,0 @@ public abstract class ChunkMapDistance {
}
+ private boolean isLoadedChunkLevel(int i) { return c(i); } // Paper - OBFHELPER
private boolean c(int i) {
return i <= this.e - 2;
}
@@ -0,0 +0,0 @@ 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 diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
@ -509,6 +580,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private int lastExpLevelScored = Integer.MIN_VALUE; private int lastExpLevelScored = Integer.MIN_VALUE;
private int lastExpTotalScored = Integer.MIN_VALUE; private int lastExpTotalScored = Integer.MIN_VALUE;
+ public long lastHighPriorityChecked; // Paper + public long lastHighPriorityChecked; // Paper
+ public void forceCheckHighPriority() {
+ lastHighPriorityChecked = -1;
+ getWorldServer().getChunkProvider().playerChunkMap.checkHighPriorityChunks(this);
+ }
+ public boolean isRealPlayer; // Paper
private float lastHealthSent = -1.0E8F; private float lastHealthSent = -1.0E8F;
private int lastFoodSent = -99999999; private int lastFoodSent = -99999999;
private boolean lastSentSaturationZero = true; private boolean lastSentSaturationZero = true;
@ -538,7 +614,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()) ((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) { for (int i = 0; i < this.inventory.getSize(); ++i) {
ItemStack itemstack = this.inventory.getItem(i); ItemStack itemstack = this.inventory.getItem(i);
@ -709,7 +785,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return CHUNK_STATUSES.get(status.getStatusIndex() + 1); + return CHUNK_STATUSES.get(status.getStatusIndex() + 1);
+ } + }
+ public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUncheckedMain(ChunkStatus chunkstatus) { + 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 // Paper end
@ -736,7 +815,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper start - cache ticking ready status // Paper start - cache ticking ready status
int expectCreateCount = ++this.fullChunkCreateCount; int expectCreateCount = ++this.fullChunkCreateCount;
- this.fullChunkFuture = playerchunkmap.b(this); this.fullChunkFuture.thenAccept((either) -> { - 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) { if (either.left().isPresent() && PlayerChunk.this.fullChunkCreateCount == expectCreateCount) {
// note: Here is a very good place to add callbacks to logic waiting on this. // note: Here is a very good place to add callbacks to logic waiting on this.
Chunk fullChunk = either.left().get(); Chunk fullChunk = either.left().get();
@ -751,7 +830,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (!flag4 && flag5) { if (!flag4 && flag5) {
// Paper start - cache ticking ready status // Paper start - cache ticking ready status
- this.tickingFuture = playerchunkmap.a(this); this.tickingFuture.thenAccept((either) -> { - 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()) { if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this. // note: Here is a very good place to add callbacks to logic waiting on this.
Chunk tickingChunk = either.left().get(); Chunk tickingChunk = either.left().get();
@ -760,7 +839,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper start - cache ticking ready status // Paper start - cache ticking ready status
- this.entityTickingFuture = playerchunkmap.b(this.location); this.entityTickingFuture.thenAccept((either) -> { - 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()) { if (either.left().isPresent()) {
// note: Here is a very good place to add callbacks to logic waiting on this. // note: Here is a very good place to add callbacks to logic waiting on this.
Chunk entityTickingChunk = either.left().get(); Chunk entityTickingChunk = either.left().get();
@ -847,7 +926,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
- }); - });
+ PlayerChunkMap.this.world.getChunkProvider().clearPriorityTickets(chunkPos); + 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.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
this.playerViewDistanceBroadcastMap = 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, (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
@ -878,33 +960,41 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return playerchunk == null || unloadQueue.contains(playerchunk.location.pair()); + 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) { + public void checkHighPriorityChunks(EntityPlayer player) {
+ int currentTick = MinecraftServer.currentTick; + int currentTick = MinecraftServer.currentTick;
+ if (currentTick - player.lastHighPriorityChecked < 20) { + if (currentTick - player.lastHighPriorityChecked < 20 || !player.isRealPlayer) { // weed out fake players
+ return; + return;
+ } + }
+ player.lastHighPriorityChecked = currentTick; + player.lastHighPriorityChecked = currentTick;
+ it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap priorities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap();
+ +
+ int viewDistance = getEffectiveNoTickViewDistance(); + int viewDistance = getEffectiveNoTickViewDistance();
+ chunkDistanceManager.delayDistanceManagerTick = true;
+ BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire(); + BlockPosition.PooledBlockPosition pos = BlockPosition.PooledBlockPosition.acquire();
+ +
+ // Prioritize circular near + // Prioritize circular near
+ double playerChunkX = MathHelper.floor(player.locX()) >> 4; + double playerChunkX = MathHelper.floor(player.locX()) >> 4;
+ double playerChunkZ = MathHelper.floor(player.locZ()) >> 4; + double playerChunkZ = MathHelper.floor(player.locZ()) >> 4;
+ pos.setValues(player.locX(), 0, player.locZ()); + pos.setValues(player.locX(), 0, player.locZ());
+ double twoThirdModifier = 2D / 3D;
+ MCUtil.getSpiralOutChunks(pos, Math.min(6, viewDistance)).forEach(coord -> { + MCUtil.getSpiralOutChunks(pos, Math.min(6, viewDistance)).forEach(coord -> {
+ if (shouldSkipPrioritization(coord)) return; + if (shouldSkipPrioritization(coord)) return;
+ +
+ double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z); + double dist = MCUtil.distance(playerChunkX, 0, playerChunkZ, coord.x, 0, coord.z);
+ // Prioritize immediate + // Prioritize immediate
+ if (dist <= 4 * 4) { + if (dist <= 4 * 4) {
+ chunkDistanceManager.markHighPriority(coord, (int) (27 - Math.sqrt(dist))); + updateChunkPriorityMap(priorities, coord.pair(), (int) (27 - Math.sqrt(dist)));
+ return; + return;
+ } + }
+ +
+ // Prioritize nearby chunks + // 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 + // Prioritize Frustum near 3
@ -913,7 +1003,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ MCUtil.getSpiralOutChunks(pos, Math.min(5, viewDistance)).forEach(coord -> { + MCUtil.getSpiralOutChunks(pos, Math.min(5, viewDistance)).forEach(coord -> {
+ if (shouldSkipPrioritization(coord)) return; + 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 + // Prioritize Frustum near 5
@ -923,7 +1014,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ MCUtil.getSpiralOutChunks(pos, 4).forEach(coord -> { + MCUtil.getSpiralOutChunks(pos, 4).forEach(coord -> {
+ if (shouldSkipPrioritization(coord)) return; + 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 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (shouldSkipPrioritization(coord)) { + if (shouldSkipPrioritization(coord)) {
+ return; + 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(); + pos.close();
+ if (priorities.isEmpty()) return;
+ chunkDistanceManager.delayDistanceManagerTick = true;
+ priorities.long2IntEntrySet().fastForEach(entry -> chunkDistanceManager.markHighPriority(new ChunkCoordIntPair(entry.getLongKey()), entry.getIntValue()));
+ chunkDistanceManager.delayDistanceManagerTick = false; + chunkDistanceManager.delayDistanceManagerTick = false;
+ world.getChunkProvider().tickDistanceManager(); + world.getChunkProvider().tickDistanceManager();
+
+ } + }
+ +
+ private boolean shouldSkipPrioritization(ChunkCoordIntPair coord) { + private boolean shouldSkipPrioritization(ChunkCoordIntPair coord) {
@ -1018,13 +1115,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/PlayerConnection.java --- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/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 { @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn {
// CraftBukkit end
this.A = this.e; 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.player.setLocation(d0, d1, d2, f, f1);
this.syncPosition(); // Paper 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)); 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 diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java --- a/src/main/java/net/minecraft/server/PlayerList.java
@ -1048,11 +1145,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}; };
}); });
} }
@@ -0,0 +0,0 @@ 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());
@@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -0,0 +0,0 @@ public abstract class PlayerList {
// CraftBukkit end // CraftBukkit end
worldserver.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper 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) { while (avoidSuffocation && !worldserver.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ()); entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
} }