5757import net .minecraft .world .level .chunk .SingleValuePalette ;
5858import net .minecraft .world .level .entity .PersistentEntitySectionManager ;
5959import org .apache .logging .log4j .Logger ;
60- import org .bukkit .Bukkit ;
6160import org .bukkit .craftbukkit .v1_20_R2 .CraftChunk ;
6261
6362import javax .annotation .Nonnull ;
7978import java .util .Map ;
8079import java .util .Optional ;
8180import java .util .concurrent .CompletableFuture ;
81+ import java .util .concurrent .ExecutionException ;
8282import java .util .concurrent .Semaphore ;
83- import java .util .concurrent .TimeUnit ;
84- import java .util .concurrent .TimeoutException ;
8583import java .util .function .IntFunction ;
8684
8785import static java .lang .invoke .MethodType .methodType ;
@@ -276,12 +274,49 @@ static DelegateSemaphore applyLock(LevelChunkSection section) {
276274 }
277275 }
278276 } catch (Throwable e ) {
279- e . printStackTrace ( );
277+ LOGGER . error ( "Error apply DelegateSemaphore" , e );
280278 throw new RuntimeException (e );
281279 }
282280 }
283281
284- public static LevelChunk ensureLoaded (ServerLevel serverLevel , int chunkX , int chunkZ ) {
282+ public static CompletableFuture <LevelChunk > ensureLoaded (ServerLevel serverLevel , int chunkX , int chunkZ ) {
283+ LevelChunk levelChunk = getChunkImmediatelyAsync (serverLevel , chunkX , chunkZ );
284+ if (levelChunk != null ) {
285+ return CompletableFuture .completedFuture (levelChunk );
286+ }
287+ if (PaperLib .isPaper ()) {
288+ CompletableFuture <LevelChunk > future = serverLevel
289+ .getWorld ()
290+ .getChunkAtAsync (chunkX , chunkZ , true , true )
291+ .thenApply (chunk -> {
292+ addTicket (serverLevel , chunkX , chunkZ );
293+ try {
294+ return (LevelChunk ) CRAFT_CHUNK_GET_HANDLE .invoke (chunk );
295+ } catch (Throwable e ) {
296+ LOGGER .error ("Could not asynchronously load chunk at {},{}" , chunkX , chunkZ , e );
297+ return null ;
298+ }
299+ });
300+ try {
301+ if (!future .isCompletedExceptionally () || (future .isDone () && future .get () != null )) {
302+ return future ;
303+ }
304+ Throwable t = future .exceptionNow ();
305+ LOGGER .error ("Asynchronous chunk load at {},{} exceptionally completed immediately" , chunkX , chunkZ , t );
306+ } catch (InterruptedException | ExecutionException e ) {
307+ LOGGER .error (
308+ "Unexpected error when getting completed future at chunk {},{}. Returning to default." ,
309+ chunkX ,
310+ chunkZ ,
311+ e
312+ );
313+ }
314+ }
315+ return CompletableFuture .supplyAsync (() -> TaskManager .taskManager ().sync (() -> serverLevel .getChunk (chunkX , chunkZ )));
316+ }
317+
318+
319+ public static @ Nullable LevelChunk getChunkImmediatelyAsync (ServerLevel serverLevel , int chunkX , int chunkZ ) {
285320 if (!PaperLib .isPaper ()) {
286321 LevelChunk nmsChunk = serverLevel .getChunkSource ().getChunk (chunkX , chunkZ , false );
287322 if (nmsChunk != null ) {
@@ -290,6 +325,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c
290325 if (Fawe .isMainThread ()) {
291326 return serverLevel .getChunk (chunkX , chunkZ );
292327 }
328+ return null ;
293329 } else {
294330 LevelChunk nmsChunk = serverLevel .getChunkSource ().getChunkAtIfCachedImmediately (chunkX , chunkZ );
295331 if (nmsChunk != null ) {
@@ -305,30 +341,8 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c
305341 if (Fawe .isMainThread ()) {
306342 return serverLevel .getChunk (chunkX , chunkZ );
307343 }
308- CompletableFuture <org .bukkit .Chunk > future = serverLevel .getWorld ().getChunkAtAsync (chunkX , chunkZ , true , true );
309- try {
310- CraftChunk chunk ;
311- try {
312- chunk = (CraftChunk ) future .get (10 , TimeUnit .SECONDS );
313- } catch (TimeoutException e ) {
314- String world = serverLevel .getWorld ().getName ();
315- // We've already taken 10 seconds we can afford to wait a little here.
316- boolean loaded = TaskManager .taskManager ().sync (() -> Bukkit .getWorld (world ) != null );
317- if (loaded ) {
318- LOGGER .warn ("Chunk {},{} failed to load in 10 seconds in world {}. Retrying..." , chunkX , chunkZ , world );
319- // Retry chunk load
320- chunk = (CraftChunk ) serverLevel .getWorld ().getChunkAtAsync (chunkX , chunkZ , true , true ).get ();
321- } else {
322- throw new UnsupportedOperationException ("Cannot load chunk from unloaded world " + world + "!" );
323- }
324- }
325- addTicket (serverLevel , chunkX , chunkZ );
326- return (LevelChunk ) CRAFT_CHUNK_GET_HANDLE .invoke (chunk );
327- } catch (Throwable e ) {
328- e .printStackTrace ();
329- }
344+ return null ;
330345 }
331- return TaskManager .taskManager ().sync (() -> serverLevel .getChunk (chunkX , chunkZ ));
332346 }
333347
334348 private static void addTicket (ServerLevel serverLevel , int chunkX , int chunkZ ) {
@@ -672,7 +686,7 @@ static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
672686 }
673687 methodremoveTickingBlockEntity .invoke (levelChunk , beacon .getBlockPos ());
674688 } catch (Throwable throwable ) {
675- throwable . printStackTrace ( );
689+ LOGGER . error ( "Error removing beacon" , throwable );
676690 }
677691 }
678692
0 commit comments