diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index 122c1c0f0..5f97d5587 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -45,6 +45,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; /** * Handles reading/writing {@link FullDataSourceV2} @@ -452,6 +453,11 @@ public class FullDataSourceProviderV2 /** @return true if the position was queued, false if not */ public boolean queuePositionForRetrieval(DhSectionPos genPos) { return false; } + /** does nothing if the given position isn't present in the queue */ + public void removeRetrievalRequestIf(Function removeIf) { } + + public void clearRetrievalQueue() { } + /** Can be used to display how many total retrieval requests might be available. */ public void setTotalRetrievalPositionCount(int newCount) { } 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 8c637ece3..dc2abe9cf 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 @@ -65,40 +65,6 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im - //==================// - // generation queue // - //==================// - - /** - * Assigns the queue for handling world gen and does first time setup as well.
- * Assumes there isn't a pre-existing queue. - */ - public void setWorldGenerationQueue(IFullDataSourceRetrievalQueue newWorldGenQueue) - { - boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); - LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level ["+this.level+"]."); - } - - public void clearGenerationQueue() { this.worldGenQueueRef.set(null); } - - /** Can be used to remove positions that are outside the player's render distance. */ - public void removeGenRequestIf(Function removeIf) - { - // TODO there has to be a better way to do this - - IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); - if (worldGenQueue != null) - { - worldGenQueue.removeGenRequestIf(removeIf); - } - } - - @Override - public int getUnsavedDataSourceCount() { return this.delayedFullDataSourceSaveCache.getUnsavedCount(); } - - - //=================// // event listeners // //=================// @@ -157,6 +123,17 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im // world gen (data source retrieval) // //===================================// + /** + * Assigns the queue for handling world gen and does first time setup as well.
+ * Assumes there isn't a pre-existing queue. + */ + public void setWorldGenerationQueue(IFullDataSourceRetrievalQueue newWorldGenQueue) + { + boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); + LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); + LOGGER.info("Set world gen queue for level ["+this.level+"]."); + } + @Override public boolean canRetrieveMissingDataSources() { return true; } @@ -226,6 +203,23 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im return true; } + @Override + public void removeRetrievalRequestIf(Function removeIf) + { + IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); + if (worldGenQueue != null) + { + worldGenQueue.removeRetrievalRequestIf(removeIf); + } + } + + @Override + public void clearRetrievalQueue() { this.worldGenQueueRef.set(null); } + + @Override + public int getUnsavedDataSourceCount() { return this.delayedFullDataSourceSaveCache.getUnsavedCount(); } + + @Override public ArrayList getPositionsToRetrieve(DhSectionPos pos) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java index 5bf47abd8..ade0d524e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java @@ -76,10 +76,11 @@ public interface IFullDataSourceRetrievalQueue extends Closeable // task handling // //===============// - /** @deprecated replace with {@link IFullDataSourceRetrievalQueue#removeGenTask(DhSectionPos)} */ - @Deprecated - void removeGenRequestIf(Function removeIf); - void removeGenTask(DhSectionPos pos); + /** + * Generally the retrieval queue should be fairly small, so its faster to iterate over the existing list + * and check if each one is valid vs dumbly attempting to remove every position that just went out of range. + */ + void removeRetrievalRequestIf(Function removeIf); CompletableFuture submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); 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 05d75ec8c..04c485bf2 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 @@ -159,7 +159,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb } @Override - public void removeGenRequestIf(Function removeIf) + public void removeRetrievalRequestIf(Function removeIf) { this.waitingTasks.forEachKey(100, (genPos) -> { @@ -170,16 +170,6 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb }); } - @Override - public void removeGenTask(DhSectionPos pos) - { - WorldGenTask task = this.waitingTasks.remove(pos); - if (task != null) - { - task.future.cancel(true); - } - } - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 1728d7825..7da152d82 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -124,13 +124,6 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev if (isWorldGenRunning) { - ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); - if (renderState != null && renderState.quadtree != null) - { - // remove any generator sections that are out of bounds - this.serverside.dataFileHandler.removeGenRequestIf(pos -> !renderState.quadtree.isSectionPosInBounds(pos)); - } - this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index ce0040165..baab12724 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -101,7 +101,7 @@ public class WorldGenModule implements Closeable return; } } - dataFileHandler.clearGenerationQueue(); + dataFileHandler.clearRetrievalQueue(); worldGenState.closeAsync(true).join(); //TODO: Make it async. dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index e619aaafe..81346e1e2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -41,6 +41,7 @@ import java.awt.*; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; /** @@ -345,6 +346,22 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable { this.renderSourceLoadingFuture.cancel(true); } + + + // remove any active world gen requests that may be for this position + ThreadPoolExecutor executor = ThreadPoolUtil.getCleanupExecutor(); + if (executor != null && !executor.isTerminated()) + { + // while this should generally be a fast operation + // this is run on a separate thread to prevent lag on the render thread + + try + { + executor.execute(() -> this.fullDataSourceProvider.removeRetrievalRequestIf((genPos) -> this.pos.contains(genPos))); + } + catch (RejectedExecutionException ignore) + { /* If this happens that means everything is already shut down and no additional cleanup will be necessary */ } + } } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index 3adacc81c..e4f786a84 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -61,6 +61,11 @@ public class ThreadPoolUtil @Nullable public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; } + public static final String CLEANUP_THREAD_NAME = "Cleanup"; + private static ThreadPoolExecutor cleanupThreadPool; + @Nullable + public static ThreadPoolExecutor getCleanupExecutor() { return cleanupThreadPool; } + //======================// @@ -108,6 +113,7 @@ public class ThreadPoolUtil updatePropagatorThreadPool = new ConfigThreadPool(UPDATE_PROPAGATOR_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfUpdatePropagatorThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads, null); worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads, null); bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME); + cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME); @@ -148,6 +154,7 @@ public class ThreadPoolUtil updatePropagatorThreadPool.shutdownExecutorService(); worldGenThreadPool.shutdownExecutorService(); bufferUploaderThreadPool.shutdown(); + cleanupThreadPool.shutdown(); // worker threads