From 006536dacfa086e2b6b76a31445527b511bd49ae Mon Sep 17 00:00:00 2001 From: mliem2k Date: Thu, 16 Jul 2026 10:06:44 +0800 Subject: [PATCH] Isolate exceptions in processTrackQueueInParallel/flushQueueInParallel Unlike tickRegion, which logs and marks a chunk-system crash on any exception (isolating the failure to a single region), the parallel tracked-entity and packet-flush futures had no .exceptionally() at all. Tracing the future chain up through ServerChunkCache.tick -> ServerLevel.tickAsync -> MinecraftServer's unguarded future.join() confirms an exception here isn't silently dropped: it propagates uncaught and crashes the whole tick loop, a much larger blast radius than the per-region isolation tickRegion already provides one method away. Add matching .exceptionally() handlers so a failure in one of these paths degrades the same way tickRegion's does instead of taking down the server. --- .../threading/ShreddedPaperChunkTicker.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/shreddedpaper-server/src/main/java/io/multipaper/shreddedpaper/threading/ShreddedPaperChunkTicker.java b/shreddedpaper-server/src/main/java/io/multipaper/shreddedpaper/threading/ShreddedPaperChunkTicker.java index f03627a..2a20ec6 100644 --- a/shreddedpaper-server/src/main/java/io/multipaper/shreddedpaper/threading/ShreddedPaperChunkTicker.java +++ b/shreddedpaper-server/src/main/java/io/multipaper/shreddedpaper/threading/ShreddedPaperChunkTicker.java @@ -74,7 +74,11 @@ private CompletableFuture processTrackQueueInParallel(ServerLevel level) { } allFuture = allFuture.thenCompose(v -> CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))); - return allFuture; + return allFuture.exceptionally(e -> { + LOGGER.error("Exception processing tracked entities in parallel", e); + MinecraftServer.getServer().moonrise$setChunkSystemCrash(new RuntimeException("Ticking thread crash while processing tracked entities in parallel", e)); + return null; + }); } finally { allFuture.whenComplete((v, e) -> level.chunkScheduler.getRegionLocker().globalLock().tryUnlockWrite()); } @@ -89,7 +93,11 @@ private CompletableFuture flushQueueInParallel(ServerLevel level) { futures.add(CompletableFuture.runAsync(() -> players.forEach(player -> player.connection.connection.flushQueue()), ShreddedPaperTickThread.getExecutor())); } - return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); + return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).exceptionally(e -> { + LOGGER.error("Exception flushing packet queues in parallel", e); + MinecraftServer.getServer().moonrise$setChunkSystemCrash(new RuntimeException("Ticking thread crash while flushing packet queues in parallel", e)); + return null; + }); } private CompletableFuture tickRegion(final ServerLevel level, final LevelChunkRegion region, final long timeInhabited, final List filteredSpawningCategories, final NaturalSpawner.SpawnState spawnState) {