Fix world gen tasks being incorrectly removed from the queue at long distances

This commit is contained in:
James Seibel
2024-04-09 19:27:57 -05:00
parent a0e7bb94c6
commit cf74d17e1b
8 changed files with 65 additions and 57 deletions
@@ -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<DhSectionPos, Boolean> removeIf) { }
public void clearRetrievalQueue() { }
/** Can be used to display how many total retrieval requests might be available. */
public void setTotalRetrievalPositionCount(int newCount) { }
@@ -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. <br>
* 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<DhSectionPos, Boolean> 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. <br>
* 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<DhSectionPos, Boolean> 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<DhSectionPos> getPositionsToRetrieve(DhSectionPos pos)
{
@@ -76,10 +76,11 @@ public interface IFullDataSourceRetrievalQueue extends Closeable
// task handling //
//===============//
/** @deprecated replace with {@link IFullDataSourceRetrievalQueue#removeGenTask(DhSectionPos)} */
@Deprecated
void removeGenRequestIf(Function<DhSectionPos, Boolean> 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<DhSectionPos, Boolean> removeIf);
CompletableFuture<WorldGenResult> submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
@@ -159,7 +159,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
}
@Override
public void removeGenRequestIf(Function<DhSectionPos, Boolean> removeIf)
public void removeRetrievalRequestIf(Function<DhSectionPos, Boolean> 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);
}
}
@@ -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()));
}
}
@@ -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);
}
@@ -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
@@ -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