replace server tick/world gen tick with a timer
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 //
|
||||
//===============//
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
{
|
||||
|
||||
+7
-11
@@ -48,7 +48,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
||||
*/
|
||||
protected final ConcurrentLinkedQueue<IServerPlayerWrapper> 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<String> 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() + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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. <br>
|
||||
* Should not return null
|
||||
|
||||
@@ -23,8 +23,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
|
||||
|
||||
public interface IDhServerLevel extends IDhLevel
|
||||
{
|
||||
void serverTick();
|
||||
|
||||
IServerLevelWrapper getServerLevelWrapper();
|
||||
|
||||
}
|
||||
|
||||
+111
-81
@@ -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<? extends AbstractWorldGenState> worldGenStateSupplier;
|
||||
private final Supplier<? extends AbstractLodRequestState> worldGenStateSupplier;
|
||||
|
||||
private final AtomicReference<AbstractWorldGenState> worldGenStateRef = new AtomicReference<>();
|
||||
private final AtomicReference<AbstractLodRequestState> 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<? extends AbstractWorldGenState> worldGenStateSupplier
|
||||
Supplier<? extends AbstractLodRequestState> 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<String> 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<Void> 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<Void> 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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+90
-47
@@ -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<BeaconBeamDTO> getAllBeamsForPos(long pos) { return this.serverLevel.beaconBeamRepo.getAllBeamsForPos(pos); }
|
||||
@@ -37,12 +40,22 @@ public class FullDataSourceRequestHandler
|
||||
private final ConcurrentMap<Long, DataSourceRequestGroup> 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<Long, DataSourceRequestGroup> 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<Long, DataSourceRequestGroup> 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -117,18 +117,6 @@ public abstract class AbstractDhServerWorld<TDhServerLevel extends AbstractDhSer
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public void serverTick() { this.dhLevelByLevelWrapper.values().forEach(TDhServerLevel::serverTick); }
|
||||
|
||||
@Override
|
||||
public void worldGenTick() { this.dhLevelByLevelWrapper.values().forEach(TDhServerLevel::worldGenTick); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@@ -115,9 +115,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
@Override
|
||||
public void clientTick() { this.eventLoop.tick(); }
|
||||
|
||||
@Override
|
||||
public void worldGenTick() { this.levels.values().forEach(DhClientLevel::worldGenTick); }
|
||||
|
||||
@Override
|
||||
public void addDebugMenuStringsToList(List<String> messageList)
|
||||
{
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -39,6 +39,4 @@ public interface IDhWorld extends Closeable
|
||||
|
||||
void unloadLevel(@NotNull ILevelWrapper levelWrapper);
|
||||
|
||||
void worldGenTick();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user