From 0636712f800e7d9f5e5c6fb6b4ef87e9e0fe2769 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 3 Feb 2026 21:32:10 -0600 Subject: [PATCH] Fix a rare world gen phantom closing issue --- .../GeneratedFullDataSourceProvider.java | 40 +++++++++++-------- .../core/generation/WorldGenerationQueue.java | 3 ++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index c6a61492a..da94a8222 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -64,9 +64,6 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im * Having this number too high causes the system to become overwhelmed by * world gen requests and other jobs won't be done.
* IE: LODs won't update or render because world gen is hogging the CPU. - *

- * TODO this should be dynamically allocated based on CPU load - * and abilities. */ public static final int MAX_WORLD_GEN_REQUESTS_PER_THREAD = 20; @@ -78,6 +75,8 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSaveAsync, 10_000); + private final ConcurrentHashMap> queuedRetrievalFutureByPos = new ConcurrentHashMap<>(); + //=============// @@ -136,7 +135,17 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im LodUtil.assertTrue(genTaskResult.dataSource != null, "Successful retrieval object should have a datasource."); this.dataUpdater.updateDataSource(genTaskResult.dataSource); - this.fireOnGenPosSuccessListeners(genTaskResult.pos); + + // synchronized to prevent a rare issue where the world generator is being shut down while this listener is firing + synchronized (this.onWorldGenTaskCompleteListeners) + { + // fire the event listeners + for (IOnWorldGenCompleteListener listener : this.onWorldGenTaskCompleteListeners) + { + listener.onWorldGenTaskComplete(genTaskResult.pos); + } + } + genTaskResult.dataSource.close(); } else if (genTaskResult.state == ERetrievalResultState.REQUIRES_SPLITTING) @@ -154,19 +163,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { LOGGER.error("Unexpected issue during onWorldGenTaskComplete, error: ["+e.getMessage()+"].", e); } - } - - // TODO only fire after the section has finished generated or once every X seconds - private void fireOnGenPosSuccessListeners(long pos) - { - // synchronized to prevent a rare issue where the world generator is being shut down while this listener is firing - synchronized (this.onWorldGenTaskCompleteListeners) + finally { - // fire the event listeners - for (IOnWorldGenCompleteListener listener : this.onWorldGenTaskCompleteListeners) - { - listener.onWorldGenTaskComplete(pos); - } + this.queuedRetrievalFutureByPos.remove(genPos); } } @@ -306,7 +305,14 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } CompletableFuture worldGenFuture = worldGenQueue.submitRetrievalTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)); - worldGenFuture.whenComplete((r, e) -> this.onWorldGenTaskComplete(genPos, r, e)); + + // only queue the when-complete once for each world gen task, + // otherwise we can end up trying to close the same datasource multiple times + CompletableFuture oldWorldGenFuture = this.queuedRetrievalFutureByPos.putIfAbsent(genPos, worldGenFuture); + if (oldWorldGenFuture == null) + { + worldGenFuture.whenComplete((r, e) -> this.onWorldGenTaskComplete(genPos, r, e)); + } return worldGenFuture; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 464e1f814..71d45101e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -138,6 +138,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb DataSourceRetrievalTask existingGenTask = this.waitingTasks.get(pos); if (existingGenTask != null) { + // if the same future is returned + // the caller shouldn't close the datasource multiple times, + // otherwise issues will occur (holes and/or out-of-place LOD data) return existingGenTask.future; }