diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 2ae7ebf1f..064c3672b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -338,12 +338,6 @@ public class ClientApi if (clientWorld != null) { clientWorld.clientTick(); - - // Ignore local world gen, as it's managed by server ticking - if (!(clientWorld instanceof DhClientServerWorld)) - { - SharedApi.worldGenTick(clientWorld::worldGenTick); - } } } catch (Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 41967ef6e..a44be7b17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -53,30 +53,6 @@ public class ServerApi - //=============// - // tick events // - //=============// - - public void serverTickEvent() - { - try - { - IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld(); - if (serverWorld != null) - { - serverWorld.serverTick(); - SharedApi.worldGenTick(serverWorld::worldGenTick); - } - } - catch (Exception e) - { - // try catch is necessary to prevent crashing the internal server when an exception is thrown - LOGGER.error("ServerTickEvent error: " + e.getMessage(), e); - } - } - - - //===============// // server events // //===============// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 25da9cfab..9b55ed25d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -141,16 +141,6 @@ public class SharedApi } } - public static void worldGenTick(Runnable worldGenRunnable) - { - lastWorldGenTickDelta--; - if (lastWorldGenTickDelta <= 0) - { - worldGenRunnable.run(); - lastWorldGenTickDelta = 20; - } - } - @Nullable public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index b3dbae741..2aaa265c7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.file.structure.ISaveStructure; import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.level.WorldGenModule; +import com.seibel.distanthorizons.core.level.LodRequestModule; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue; import com.seibel.distanthorizons.core.logging.DhLogger; @@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit; /** * Only handles {@link SyncOnLoadRequestQueue} requests (IE updating existing LODs based on a timestamp). - * Missing data is handled by {@link WorldGenModule} and {@link RemoteWorldRetrievalQueue}. + * Missing data is handled by {@link LodRequestModule} and {@link RemoteWorldRetrievalQueue}. */ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvider { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index ac7cb485f..5a53bde5f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -48,7 +48,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I */ protected final ConcurrentLinkedQueue worldGenPlayerCenteringQueue = new ConcurrentLinkedQueue<>(); - private final FullDataSourceRequestHandler requestHandler = new FullDataSourceRequestHandler(this); + private final FullDataSourceRequestHandler requestHandler; + //=============// @@ -81,6 +82,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I LOGGER.info("Started "+this.getClass().getSimpleName()+" for ["+serverLevelWrapper+"] at ["+saveStructure+"]."); this.serverPlayerStateManager = serverPlayerStateManager; + this.requestHandler = new FullDataSourceRequestHandler(this); } @@ -89,12 +91,6 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I // ticks // //=======// - @Override - public void serverTick() - { - this.requestHandler.tick(); - } - @Override public boolean shouldDoWorldGen() { return Config.Common.WorldGenerator.enableDistantGeneration.get() && !this.worldGenPlayerCenteringQueue.isEmpty(); } @@ -118,9 +114,6 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I return new DhBlockPos2D((int) position.x, (int) position.z); } - @Override - public void worldGenTick() { this.serverside.worldGenModule.worldGenTick(); } - //==================// @@ -297,7 +290,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I public void addDebugMenuStringsToList(List messageList) { this.serverside.fullDataFileHandler.addDebugMenuStringsToList(messageList); - this.serverside.worldGenModule.addDebugMenuStringsToList(messageList); + this.serverside.lodRequestModule.addDebugMenuStringsToList(messageList); } @@ -330,7 +323,10 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I { super.close(); this.serverside.close(); + this.requestHandler.close(); LOGGER.info("Closed DHLevel for [" + this.getLevelWrapper() + "]."); } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index fef19a6da..21554c235 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -83,7 +83,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel .asMap() ); - public final WorldGenModule worldGenModule; + public final LodRequestModule lodRequestModule; @Nullable private final SyncOnLoadRequestQueue syncOnLoadRequestQueue; @@ -131,7 +131,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel } this.remoteDataSourceProvider = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride, this.syncOnLoadRequestQueue); - this.worldGenModule = new WorldGenModule(this, this.remoteDataSourceProvider, () -> new WorldGenState(this, networkState)); + this.lodRequestModule = new LodRequestModule(this,this, this.remoteDataSourceProvider, () -> new LodRequestState(this, networkState)); this.clientside = new ClientLevelModule(this); @@ -239,11 +239,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Nullable public DhBlockPos2D getTargetPosForGeneration() { return new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()); } - @Override - public void worldGenTick() { this.worldGenModule.worldGenTick(); } - - public void startRenderer() { this.clientside.startRenderer(); } - //===========// @@ -325,7 +320,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel // world gen - this.worldGenModule.addDebugMenuStringsToList(messageList); + this.lodRequestModule.addDebugMenuStringsToList(messageList); if (this.syncOnLoadRequestQueue != null) { assert this.networkState != null; @@ -348,9 +343,9 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override public void close() { - if (this.worldGenModule != null) + if (this.lodRequestModule != null) { - this.worldGenModule.close(); + this.lodRequestModule.close(); } if (this.networkEventSource != null) @@ -371,11 +366,11 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel // helper classes // //================// - private static class WorldGenState extends WorldGenModule.AbstractWorldGenState + private static class LodRequestState extends LodRequestModule.AbstractLodRequestState { - WorldGenState(DhClientLevel level, ClientNetworkState networkState) + LodRequestState(DhClientLevel level, ClientNetworkState networkState) { - this.worldGenerationQueue = new RemoteWorldRetrievalQueue(networkState, level); + this.retrievalQueue = new RemoteWorldRetrievalQueue(networkState, level); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 7178111cc..ea01a0d5b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -56,9 +56,6 @@ import java.util.concurrent.CompletableFuture; */ public interface IDhLevel extends AutoCloseable, GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener { - @Deprecated - void worldGenTick(); - /** * May return either a client or server level wrapper.
* Should not return null diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java index ac2e28c1c..c4962956c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java @@ -23,8 +23,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp public interface IDhServerLevel extends IDhLevel { - void serverTick(); - IServerLevelWrapper getServerLevelWrapper(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/LodRequestModule.java similarity index 76% rename from core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java rename to core/src/main/java/com/seibel/distanthorizons/core/level/LodRequestModule.java index c1d55021d..988a55787 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/LodRequestModule.java @@ -47,16 +47,17 @@ import java.util.function.Supplier; * Handles both single-player/server-side world gen and client side LOD requests. * TODO rename */ -public class WorldGenModule implements Closeable +public class LodRequestModule implements Closeable { private static final DhLogger LOGGER = new DhLoggerBuilder().build(); private final GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener; + private final ThreadPoolExecutor tickerThread; private final GeneratedFullDataSourceProvider dataSourceProvider; - private final Supplier worldGenStateSupplier; + private final Supplier worldGenStateSupplier; - private final AtomicReference worldGenStateRef = new AtomicReference<>(); + private final AtomicReference lodRequestStateRef = new AtomicReference<>(); @@ -64,59 +65,41 @@ public class WorldGenModule implements Closeable // constructor // //=============// - public WorldGenModule( + public LodRequestModule( + IDhLevel level, GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener, GeneratedFullDataSourceProvider dataSourceProvider, - Supplier worldGenStateSupplier + Supplier worldGenStateSupplier ) { this.onWorldGenCompleteListener = onWorldGenCompleteListener; this.dataSourceProvider = dataSourceProvider; this.worldGenStateSupplier = worldGenStateSupplier; - } - - - - //===================// - // world gen control // - //===================// - - public void startWorldGen(GeneratedFullDataSourceProvider dataFileHandler, AbstractWorldGenState newWgs) - { - // create the new world generator - if (!this.worldGenStateRef.compareAndSet(null, newWgs)) - { - LOGGER.warn("Failed to start world gen due to concurrency"); - newWgs.closeAsync(false); - } - dataFileHandler.addWorldGenCompleteListener(this.onWorldGenCompleteListener); - dataFileHandler.setWorldGenerationQueue(newWgs.worldGenerationQueue); - } - - public void stopWorldGen(GeneratedFullDataSourceProvider dataFileHandler) - { - AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - LOGGER.warn("Attempted to stop world gen when it was not running"); - return; - } - // shut down the world generator - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + String levelId = level.getLevelWrapper().getDhIdentifier(); + this.tickerThread = ThreadUtil.makeSingleDaemonThreadPool("Request Module Ticker ["+levelId+"]"); + this.tickerThread.execute(this::tickLoop); + } + + + + //=========// + // ticking // + //=========// + + private void tickLoop() + { + try { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) + while (!Thread.interrupted()) { - return; + Thread.sleep(20); + this.tick(); } } - dataFileHandler.clearRetrievalQueue(); - worldGenState.closeAsync(true).join(); //TODO: Make it async. - dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener); + catch (InterruptedException ignore) { } } - - public void worldGenTick() + private void tick() { boolean shouldDoWorldGen = this.onWorldGenCompleteListener.shouldDoWorldGen(); // if the world is read only don't generate anything @@ -136,13 +119,13 @@ public class WorldGenModule implements Closeable if (this.isWorldGenRunning()) { - AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) + AbstractLodRequestState lodRequestState = this.lodRequestStateRef.get(); + if (lodRequestState != null) { DhBlockPos2D targetPosForGeneration = this.onWorldGenCompleteListener.getTargetPosForGeneration(); if (targetPosForGeneration != null) { - worldGenState.startGenerationQueueAndSetTargetPos(targetPosForGeneration); + lodRequestState.startRequestQueueAndSetTargetPos(targetPosForGeneration); } } } @@ -150,6 +133,48 @@ public class WorldGenModule implements Closeable + //===================// + // world gen control // + //===================// + + public void startWorldGen(GeneratedFullDataSourceProvider dataFileHandler, AbstractLodRequestState newWgs) + { + // create the new world generator + if (!this.lodRequestStateRef.compareAndSet(null, newWgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + newWgs.closeAsync(false); + } + + dataFileHandler.addWorldGenCompleteListener(this.onWorldGenCompleteListener); + dataFileHandler.setWorldGenerationQueue(newWgs.retrievalQueue); + } + + public void stopWorldGen(GeneratedFullDataSourceProvider dataFileHandler) + { + AbstractLodRequestState worldGenState = this.lodRequestStateRef.get(); + if (worldGenState == null) + { + LOGGER.warn("Attempted to stop world gen when it was not running"); + return; + } + + // shut down the world generator + while (!this.lodRequestStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.lodRequestStateRef.get(); + if (worldGenState == null) + { + return; + } + } + dataFileHandler.clearRetrievalQueue(); + worldGenState.closeAsync(true).join(); //TODO: Make it async. + dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener); + } + + + //=======================// // base method overrides // //=======================// @@ -157,13 +182,15 @@ public class WorldGenModule implements Closeable @Override public void close() { + this.tickerThread.shutdownNow(); + // shutdown the world-gen - AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + AbstractLodRequestState worldGenState = this.lodRequestStateRef.get(); if (worldGenState != null) { - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + while (!this.lodRequestStateRef.compareAndSet(worldGenState, null)) { - worldGenState = this.worldGenStateRef.get(); + worldGenState = this.lodRequestStateRef.get(); if (worldGenState == null) { break; @@ -183,12 +210,12 @@ public class WorldGenModule implements Closeable // getters // //=========// - public boolean isWorldGenRunning() { return this.worldGenStateRef.get() != null; } + public boolean isWorldGenRunning() { return this.lodRequestStateRef.get() != null; } /** mutates a list so it can be added to an existing {@link IDhLevel}'s debug list */ public void addDebugMenuStringsToList(List messageList) { - AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + AbstractLodRequestState worldGenState = this.lodRequestStateRef.get(); if (worldGenState == null) { return; @@ -196,9 +223,9 @@ public class WorldGenModule implements Closeable // estimated tasks - String waitingCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getWaitingTaskCount()); - String inProgressCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getInProgressTaskCount()); - String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getRetrievalEstimatedRemainingChunkCount()); + String waitingCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.retrievalQueue.getWaitingTaskCount()); + String inProgressCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.retrievalQueue.getInProgressTaskCount()); + String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.retrievalQueue.getRetrievalEstimatedRemainingChunkCount()); String message = "World Gen/Import Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress "+inProgressCountStr+")"; // estimated chunks/sec @@ -210,7 +237,7 @@ public class WorldGenModule implements Closeable messageList.add(message); - worldGenState.worldGenerationQueue.addDebugMenuStringsToList(messageList); + worldGenState.retrievalQueue.addDebugMenuStringsToList(messageList); } @@ -220,40 +247,22 @@ public class WorldGenModule implements Closeable //================// /** Handles the {@link IFullDataSourceRetrievalQueue} and any other necessary world gen information. */ - public static abstract class AbstractWorldGenState + public static abstract class AbstractLodRequestState { /** static so we only send the disable message once per session */ private static long firstProgressMessageSentMs = 0; - public IFullDataSourceRetrievalQueue worldGenerationQueue; + public IFullDataSourceRetrievalQueue retrievalQueue; private static final ThreadPoolExecutor PROGRESS_UPDATER_THREAD = ThreadUtil.makeSingleDaemonThreadPool("World Gen Progress Updater"); private boolean progressUpdateThreadRunning = false; - CompletableFuture closeAsync(boolean doInterrupt) - { - // this should stop the updater thread - this.progressUpdateThreadRunning = false; - - return this.worldGenerationQueue.startClosingAsync(true, doInterrupt) - .exceptionally(e -> - { - LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e); - return null; - } - ).thenRun(this.worldGenerationQueue::close) - .exceptionally(e -> - { - LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e); - return null; - }); - } - /** @param targetPosForGeneration the position that world generation should be centered around */ - public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPosForGeneration) + /** @param targetPosForRequest the position that world generation should be centered around */ + public void startRequestQueueAndSetTargetPos(DhBlockPos2D targetPosForRequest) { - this.worldGenerationQueue.startAndSetTargetPos(targetPosForGeneration); + this.retrievalQueue.startAndSetTargetPos(targetPosForRequest); this.startProgressUpdateThread(); } private void startProgressUpdateThread() @@ -286,8 +295,8 @@ public class WorldGenModule implements Closeable private void sendRetrievalProgress() { // format the remaining chunks - int remainingChunkCount = this.worldGenerationQueue.getRetrievalEstimatedRemainingChunkCount(); - remainingChunkCount += this.worldGenerationQueue.getQueuedChunkCount(); + int remainingChunkCount = this.retrievalQueue.getRetrievalEstimatedRemainingChunkCount(); + remainingChunkCount += this.retrievalQueue.getQueuedChunkCount(); String remainingChunkCountStr = F3Screen.NUMBER_FORMAT.format(remainingChunkCount); String message = "DH is generating chunks. " + remainingChunkCountStr + " left."; @@ -350,7 +359,7 @@ public class WorldGenModule implements Closeable /** @return -1 if this method isn't supported or available */ public double getEstimatedChunksPerSecond() { - RollingAverage avg = this.worldGenerationQueue.getRollingAverageChunkGenTimeInMs(); + RollingAverage avg = this.retrievalQueue.getRollingAverageChunkGenTimeInMs(); if (avg == null) { return -1; @@ -373,6 +382,27 @@ public class WorldGenModule implements Closeable return chunksPerSecond; } + + CompletableFuture closeAsync(boolean doInterrupt) + { + // this should stop the updater thread + this.progressUpdateThreadRunning = false; + + return this.retrievalQueue.startClosingAsync(true, doInterrupt) + .exceptionally(e -> + { + LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e); + return null; + } + ).thenRun(this.retrievalQueue::close) + .exceptionally(e -> + { + LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e); + return null; + }); + } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java index 51dfb5700..4f2212363 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java @@ -36,7 +36,7 @@ public class ServerLevelModule implements AutoCloseable public final ISaveStructure saveStructure; public final GeneratedFullDataSourceProvider fullDataFileHandler; - public final WorldGenModule worldGenModule; + public final LodRequestModule lodRequestModule; @@ -49,7 +49,7 @@ public class ServerLevelModule implements AutoCloseable this.parentServerLevel = parentServerLevel; this.saveStructure = saveStructure; this.fullDataFileHandler = new GeneratedFullDataSourceProvider(parentServerLevel, saveStructure); - this.worldGenModule = new WorldGenModule(this.parentServerLevel, this.fullDataFileHandler, () -> new ServerLevelModule.WorldGenState(this.parentServerLevel)); + this.lodRequestModule = new LodRequestModule(this.parentServerLevel, this.parentServerLevel, this.fullDataFileHandler, () -> new LodRequestState(this.parentServerLevel)); } @@ -62,7 +62,7 @@ public class ServerLevelModule implements AutoCloseable public void close() { // shutdown the world-gen - this.worldGenModule.close(); + this.lodRequestModule.close(); this.fullDataFileHandler.close(); } @@ -72,9 +72,9 @@ public class ServerLevelModule implements AutoCloseable // helper classes // //================// - public static class WorldGenState extends WorldGenModule.AbstractWorldGenState + public static class LodRequestState extends LodRequestModule.AbstractLodRequestState { - WorldGenState(IDhServerLevel level) + LodRequestState(IDhServerLevel level) { IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); if (worldGenerator == null) @@ -85,7 +85,7 @@ public class ServerLevelModule implements AutoCloseable // since core world generator's should have the lowest override priority WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator); } - this.worldGenerationQueue = new WorldGenerationQueue(worldGenerator, level); + this.retrievalQueue = new WorldGenerationQueue(worldGenerator, level); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/FullDataSourceRequestHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/FullDataSourceRequestHandler.java index 2e523ea25..2ea17da30 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/FullDataSourceRequestHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/FullDataSourceRequestHandler.java @@ -14,6 +14,7 @@ import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceR import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import java.util.List; @@ -21,7 +22,7 @@ import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -public class FullDataSourceRequestHandler +public class FullDataSourceRequestHandler implements AutoCloseable { private static final DhLogger LOGGER = new DhLoggerBuilder() .fileLevelConfig(Config.Common.Logging.logNetworkEventToFile) @@ -29,6 +30,8 @@ public class FullDataSourceRequestHandler private final AbstractDhServerLevel serverLevel; + private final ThreadPoolExecutor tickerThread; + private String getLevelIdentifier() { return this.serverLevel.getLevelWrapper().getDhIdentifier(); } private GeneratedFullDataSourceProvider fullDataSourceProvider() { return this.serverLevel.serverside.fullDataFileHandler; } private List getAllBeamsForPos(long pos) { return this.serverLevel.beaconBeamRepo.getAllBeamsForPos(pos); } @@ -37,12 +40,22 @@ public class FullDataSourceRequestHandler private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>(); + + //=============// + // constructor // + //=============// + public FullDataSourceRequestHandler(AbstractDhServerLevel serverLevel) { this.serverLevel = serverLevel; + + String levelId = this.serverLevel.getServerLevelWrapper().getDhIdentifier(); + this.tickerThread = ThreadUtil.makeSingleDaemonThreadPool("DataSource Request Ticker ["+levelId+"]"); + this.tickerThread.execute(this::tickLoop); } + //==================// // network handling // //==================// @@ -214,52 +227,6 @@ public class FullDataSourceRequestHandler } } - - public void tick() - { - // Send finished data source requests - for (Map.Entry entry : this.requestGroupsByPos.entrySet()) - { - DataSourceRequestGroup requestGroup = entry.getValue(); - - if (requestGroup.fullDataSource == null) - { - continue; - } - - LOGGER.debug("[" + this.getLevelIdentifier() + "] Fulfilled request group [" + DhSectionPos.toString(entry.getKey()) + "]"); - - // Make this group unavailable for adding into - this.requestGroupsByPos.remove(entry.getKey()); - if (!requestGroup.tryClose()) - { - continue; - } - - AbstractExecutorService executor = ThreadPoolUtil.getNetworkCompressionExecutor(); - if (executor == null) - { - LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null"); - continue; - } - CompletableFuture.runAsync(() -> - { - FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource, this.getAllBeamsForPos(entry.getKey())); - requestGroup.fullDataSource.close(); - - for (DataSourceRequestGroup.RequestData requestData : requestGroup.requestMessages.values()) - { - this.requestGroupsByFutureId.remove(requestData.futureId()); - - requestData.serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> { - requestData.message.sendResponse(new FullDataSourceResponseMessage(payload)); - requestData.rateLimiterSet.generationRequestRateLimiter.release(); - }); - } - }, executor); - } - } - private void tryFulfillDataSourceRequestGroup(DataSourceRequestGroup requestGroup, long pos) { this.fullDataSourceProvider().getAsync(pos).thenAccept(fullDataSource -> @@ -313,4 +280,80 @@ public class FullDataSourceRequestHandler } } + + + //=========// + // ticking // + //=========// + + private void tickLoop() + { + try + { + while (!Thread.interrupted()) + { + Thread.sleep(20); + this.tick(); + } + } + catch (InterruptedException ignore) { } + } + private void tick() + { + // Send finished data source requests + for (Map.Entry entry : this.requestGroupsByPos.entrySet()) + { + DataSourceRequestGroup requestGroup = entry.getValue(); + if (requestGroup.fullDataSource == null) + { + continue; + } + + LOGGER.debug("[" + this.getLevelIdentifier() + "] Fulfilled request group [" + DhSectionPos.toString(entry.getKey()) + "]"); + + // Make this group unavailable for adding into + this.requestGroupsByPos.remove(entry.getKey()); + if (!requestGroup.tryClose()) + { + continue; + } + + AbstractExecutorService executor = ThreadPoolUtil.getNetworkCompressionExecutor(); + if (executor == null) + { + LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null"); + continue; + } + CompletableFuture.runAsync(() -> + { + FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource, this.getAllBeamsForPos(entry.getKey())); + requestGroup.fullDataSource.close(); + + for (DataSourceRequestGroup.RequestData requestData : requestGroup.requestMessages.values()) + { + this.requestGroupsByFutureId.remove(requestData.futureId()); + + requestData.serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> { + requestData.message.sendResponse(new FullDataSourceResponseMessage(payload)); + requestData.rateLimiterSet.generationRequestRateLimiter.release(); + }); + } + }, executor); + } + } + + + + //================// + // base overrides // + //================// + + @Override + public void close() + { + this.tickerThread.shutdownNow(); + } + + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index 9d2a160ca..77999786c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -117,18 +117,6 @@ public abstract class AbstractDhServerWorld messageList) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhServerWorld.java index 63629f752..40e4a1e28 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhServerWorld.java @@ -32,7 +32,6 @@ public interface IDhServerWorld extends IDhWorld void addPlayer(IServerPlayerWrapper serverPlayer); void removePlayer(IServerPlayerWrapper serverPlayer); void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel); - void serverTick(); default IDhServerLevel getOrLoadServerLevel(ILevelWrapper levelWrapper) { return (IDhServerLevel) this.getOrLoadLevel(levelWrapper); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java index 8b08827a3..97d2c050e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java @@ -39,6 +39,4 @@ public interface IDhWorld extends Closeable void unloadLevel(@NotNull ILevelWrapper levelWrapper); - void worldGenTick(); - }