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;
}