mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
Re-add legacy getChunkAtAsynchronously to ChunkProviderServer
Apparently some plugins use it
This commit is contained in:
parent
0ee53db50a
commit
a2df674cc0
3 changed files with 95 additions and 93 deletions
|
@ -2587,103 +2587,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
+ // Paper start - async chunk io
|
+ // Paper start - async chunk io
|
||||||
+ private long asyncLoadSeqCounter;
|
|
||||||
+
|
|
||||||
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) {
|
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) {
|
||||||
+ if (Thread.currentThread() != this.mainThread) {
|
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> ret = new CompletableFuture<>();
|
||||||
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = new CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>();
|
+
|
||||||
+ this.mainThreadProcessor.execute(() -> {
|
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority;
|
||||||
+ this.getChunkAtAsynchronously(x, z, gen, isUrgent).whenComplete((chunk, ex) -> {
|
+ if (isUrgent) {
|
||||||
+ if (ex != null) {
|
+ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER;
|
||||||
+ future.completeExceptionally(ex);
|
|
||||||
+ } else {
|
+ } else {
|
||||||
+ future.complete(chunk);
|
+ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ net.minecraft.server.ChunkSystem.scheduleChunkLoad(this.level, x, z, gen, ChunkStatus.FULL, true, priority, (chunk) -> {
|
||||||
|
+ if (chunk == null) {
|
||||||
|
+ ret.complete(ChunkHolder.UNLOADED_CHUNK);
|
||||||
|
+ } else {
|
||||||
|
+ ret.complete(Either.left(chunk));
|
||||||
+ }
|
+ }
|
||||||
+ });
|
+ });
|
||||||
+ });
|
|
||||||
+ return future;
|
|
||||||
+ }
|
|
||||||
+
|
+
|
||||||
+ long k = ChunkPos.asLong(x, z);
|
+ return ret;
|
||||||
+ ChunkPos chunkPos = new ChunkPos(x, z);
|
|
||||||
+
|
|
||||||
+ ChunkAccess ichunkaccess;
|
|
||||||
+
|
|
||||||
+ // try cache
|
|
||||||
+ for (int l = 0; l < 4; ++l) {
|
|
||||||
+ if (k == this.lastChunkPos[l] && ChunkStatus.FULL == this.lastChunkStatus[l]) {
|
|
||||||
+ ichunkaccess = this.lastChunk[l];
|
|
||||||
+ if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
|
|
||||||
+
|
|
||||||
+ // move to first in cache
|
|
||||||
+
|
|
||||||
+ for (int i1 = 3; i1 > 0; --i1) {
|
|
||||||
+ this.lastChunkPos[i1] = this.lastChunkPos[i1 - 1];
|
|
||||||
+ this.lastChunkStatus[i1] = this.lastChunkStatus[i1 - 1];
|
|
||||||
+ this.lastChunk[i1] = this.lastChunk[i1 - 1];
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ this.lastChunkPos[0] = k;
|
|
||||||
+ this.lastChunkStatus[0] = ChunkStatus.FULL;
|
|
||||||
+ this.lastChunk[0] = ichunkaccess;
|
|
||||||
+
|
|
||||||
+ return CompletableFuture.completedFuture(Either.left(ichunkaccess));
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (gen) {
|
|
||||||
+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions
|
|
||||||
+ if (current != null) {
|
|
||||||
+ if (!(current instanceof net.minecraft.world.level.chunk.ImposterProtoChunk) && !(current instanceof LevelChunk)) {
|
|
||||||
+ return CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK);
|
|
||||||
+ }
|
|
||||||
+ // we know the chunk is at full status here (either in read-only mode or the real thing)
|
|
||||||
+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // here we don't know what status it is and we're not supposed to generate
|
|
||||||
+ // so we asynchronously load empty status
|
|
||||||
+ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, isUrgent).thenCompose((either) -> {
|
|
||||||
+ ChunkAccess chunk = either.left().orElse(null);
|
|
||||||
+ if (!(chunk instanceof net.minecraft.world.level.chunk.ImposterProtoChunk) && !(chunk instanceof LevelChunk)) {
|
|
||||||
+ // the chunk on disk was not a full status chunk
|
|
||||||
+ return CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK);
|
|
||||||
+ }
|
|
||||||
+ // bring to full status if required
|
|
||||||
+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> bringToFullStatusAsync(int x, int z, ChunkPos chunkPos, boolean isUrgent) {
|
|
||||||
+ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, isUrgent);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> bringToStatusAsync(int x, int z, ChunkPos chunkPos, ChunkStatus status, boolean isUrgent) {
|
|
||||||
+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getChunkFutureMainThread(x, z, status, true, isUrgent);
|
|
||||||
+ Long identifier = Long.valueOf(this.asyncLoadSeqCounter++);
|
|
||||||
+ int ticketLevel = net.minecraft.server.MCUtil.getTicketLevelFor(status);
|
|
||||||
+ this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
|
||||||
+
|
|
||||||
+ return future.thenComposeAsync((Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either) -> {
|
|
||||||
+ // either left -> success
|
|
||||||
+ // either right -> failure
|
|
||||||
+
|
|
||||||
+ this.removeTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
|
||||||
+ this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); // allow unloading
|
|
||||||
+
|
|
||||||
+ Optional<ChunkHolder.ChunkLoadingFailure> failure = either.right();
|
|
||||||
+
|
|
||||||
+ if (failure.isPresent()) {
|
|
||||||
+ // failure
|
|
||||||
+ throw new IllegalStateException("Chunk failed to load: " + failure.get().toString());
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return CompletableFuture.completedFuture(either);
|
|
||||||
+ }, this.mainThreadProcessor);
|
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end - async chunk io
|
+ // Paper end - async chunk io
|
||||||
|
|
||||||
|
|
|
@ -798,8 +798,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||||
return CompletableFuture.completedFuture(either);
|
|
||||||
}, this.mainThreadProcessor);
|
return ret;
|
||||||
}
|
}
|
||||||
+
|
+
|
||||||
+ public boolean markUrgent(ChunkPos coords) {
|
+ public boolean markUrgent(ChunkPos coords) {
|
||||||
|
|
|
@ -6022,6 +6022,86 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
|
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, onLoad
|
||||||
+ );
|
+ );
|
||||||
+ }
|
+ }
|
||||||
|
+
|
||||||
|
+ void chunkLoadAccept(int chunkX, int chunkZ, ChunkAccess chunk, java.util.function.Consumer<ChunkAccess> consumer) {
|
||||||
|
+ try {
|
||||||
|
+ consumer.accept(chunk);
|
||||||
|
+ } catch (Throwable throwable) {
|
||||||
|
+ if (throwable instanceof ThreadDeath) {
|
||||||
|
+ throw (ThreadDeath)throwable;
|
||||||
|
+ }
|
||||||
|
+ LOGGER.error("Load callback for chunk " + chunkX + "," + chunkZ + " in world '" + this.level.getWorld().getName() + "' threw an exception", throwable);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ void getChunkAtAsynchronously(int chunkX, int chunkZ, int ticketLevel,
|
||||||
|
+ java.util.function.Consumer<ChunkAccess> consumer) {
|
||||||
|
+ if (ticketLevel <= 33) {
|
||||||
|
+ this.getFullChunkAsync(chunkX, chunkZ, (java.util.function.Consumer)consumer);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ net.minecraft.server.ChunkSystem.scheduleChunkLoad(
|
||||||
|
+ this.level, chunkX, chunkZ, ChunkHolder.getStatus(ticketLevel), true,
|
||||||
|
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL, consumer
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ public final void getChunkAtAsynchronously(int chunkX, int chunkZ, ChunkStatus status, boolean gen, boolean allowSubTicketLevel, java.util.function.Consumer<ChunkAccess> onLoad) {
|
||||||
|
+ // try to fire sync
|
||||||
|
+ int chunkStatusTicketLevel = 33 + ChunkStatus.getDistance(status);
|
||||||
|
+ ChunkHolder playerChunk = this.chunkMap.getUpdatingChunkIfPresent(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||||
|
+ if (playerChunk != null) {
|
||||||
|
+ ChunkStatus holderStatus = playerChunk.getChunkHolderStatus();
|
||||||
|
+ ChunkAccess immediate = playerChunk.getAvailableChunkNow();
|
||||||
|
+ if (immediate != null) {
|
||||||
|
+ if (allowSubTicketLevel ? immediate.getStatus().isOrAfter(status) : (playerChunk.getTicketLevel() <= chunkStatusTicketLevel && holderStatus != null && holderStatus.isOrAfter(status))) {
|
||||||
|
+ this.chunkLoadAccept(chunkX, chunkZ, immediate, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ } else {
|
||||||
|
+ if (gen || (!allowSubTicketLevel && immediate.getStatus().isOrAfter(status))) {
|
||||||
|
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ } else {
|
||||||
|
+ this.chunkLoadAccept(chunkX, chunkZ, null, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // need to fire async
|
||||||
|
+
|
||||||
|
+ if (gen && !allowSubTicketLevel) {
|
||||||
|
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ this.getChunkAtAsynchronously(chunkX, chunkZ, net.minecraft.server.MCUtil.getTicketLevelFor(ChunkStatus.EMPTY), (ChunkAccess chunk) -> {
|
||||||
|
+ if (chunk == null) {
|
||||||
|
+ throw new IllegalStateException("Chunk cannot be null");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!chunk.getStatus().isOrAfter(status)) {
|
||||||
|
+ if (gen) {
|
||||||
|
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ } else {
|
||||||
|
+ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, null, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ if (allowSubTicketLevel) {
|
||||||
|
+ ServerChunkCache.this.chunkLoadAccept(chunkX, chunkZ, chunk, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ } else {
|
||||||
|
+ this.getChunkAtAsynchronously(chunkX, chunkZ, chunkStatusTicketLevel, onLoad);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
+ // Paper end
|
+ // Paper end
|
||||||
+
|
+
|
||||||
+ // Paper start
|
+ // Paper start
|
||||||
|
|
Loading…
Reference in a new issue