From f11752da96c0c366dc8aa0b02c10e4d3168d7374 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 13 Nov 2022 16:33:30 -0600 Subject: [PATCH] Refactor Dh Client/Server/ClientServer Level objects --- .../seibel/lod/core/level/DhClientLevel.java | 151 +++-- .../lod/core/level/DhClientServerLevel.java | 616 ++++++++++-------- .../seibel/lod/core/level/DhServerLevel.java | 117 ++-- 3 files changed, 487 insertions(+), 397 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/lod/core/level/DhClientLevel.java index 57ad748c0..fb78c5250 100644 --- a/core/src/main/java/com/seibel/lod/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/DhClientLevel.java @@ -26,75 +26,84 @@ import java.util.concurrent.CompletableFuture; public class DhClientLevel implements IDhClientLevel { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public final ClientOnlySaveStructure save; - public final RemoteDataFileHandler dataFileHandler; - public final RenderFileHandler renderFileHandler; - public final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer? - public final IClientLevelWrapper level; - public LodRenderer renderer = null; - public LodQuadTree tree; - - public DhClientLevel(ClientOnlySaveStructure save, IClientLevelWrapper level) { - this.save = save; - save.getDataFolder(level).mkdirs(); - save.getRenderCacheFolder(level).mkdirs(); - dataFileHandler = new RemoteDataFileHandler(this, save.getDataFolder(level)); - renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(level)); - tree = new LodQuadTree(this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get()*16, - MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler); - renderBufferHandler = new RenderBufferHandler(tree); - this.level = level; - FileScanUtil.scanFile(save, level, dataFileHandler, renderFileHandler); - LOGGER.info("Started DHLevel for {} with saves at {}", level, save); - } - - @Override - public void dumpRamUsage() { - //TODO - } - - @Override - public void clientTick() { - tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); - renderBufferHandler.update(); - } - - @Override - public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { - if (renderer == null) { - renderer = new LodRenderer(renderBufferHandler); - } - renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); - } - - @Override - public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { - return 0; //TODO - } - - @Override - public IClientLevelWrapper getClientLevelWrapper() { return level; } - - @Override - public ILevelWrapper getLevelWrapper() { return this.level; } - - @Override - public void updateChunk(IChunkWrapper chunk) { - //TODO - } - - @Override - public int getMinY() { return level.getMinHeight(); } - - @Override - public CompletableFuture save() { return renderFileHandler.flushAndSave(); } - - @Override - public void close() { - renderFileHandler.close(); - LOGGER.info("Closed DHLevel for {}", level); - } - + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + public final ClientOnlySaveStructure save; + public final RemoteDataFileHandler dataFileHandler; + public final RenderFileHandler renderFileHandler; + public final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer? + public final IClientLevelWrapper level; + public LodRenderer renderer = null; + public LodQuadTree tree; + + + + public DhClientLevel(ClientOnlySaveStructure save, IClientLevelWrapper level) + { + this.save = save; + save.getDataFolder(level).mkdirs(); + save.getRenderCacheFolder(level).mkdirs(); + this.dataFileHandler = new RemoteDataFileHandler(this, save.getDataFolder(level)); + this.renderFileHandler = new RenderFileHandler(this.dataFileHandler, this, save.getRenderCacheFolder(level)); + this.tree = new LodQuadTree(this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16, + MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, this.renderFileHandler); + this.renderBufferHandler = new RenderBufferHandler(this.tree); + this.level = level; + FileScanUtil.scanFile(save, level, this.dataFileHandler, this.renderFileHandler); + LOGGER.info("Started DHLevel for {} with saves at {}", level, save); + } + + + + @Override + public void dumpRamUsage() + { + //TODO + } + + @Override + public void clientTick() + { + this.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + this.renderBufferHandler.update(); + } + + @Override + public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) + { + if (this.renderer == null) + { + this.renderer = new LodRenderer(this.renderBufferHandler); + } + this.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); + } + + @Override + public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return 0; /* TODO */ } + + @Override + public IClientLevelWrapper getClientLevelWrapper() { return this.level; } + + @Override + public ILevelWrapper getLevelWrapper() { return this.level; } + + @Override + public void updateChunk(IChunkWrapper chunk) + { + //TODO + } + + @Override + public int getMinY() { return this.level.getMinHeight(); } + + @Override + public CompletableFuture save() { return this.renderFileHandler.flushAndSave(); } + + @Override + public void close() + { + this.renderFileHandler.close(); + LOGGER.info("Closed DHLevel for {}", this.level); + } + } diff --git a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java index 039313450..6c7da4211 100644 --- a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java @@ -36,273 +36,355 @@ import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; +/** The level used on a singleplayer world */ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel { - class RenderState { - final IClientLevelWrapper clientLevel; - final LodQuadTree tree; - final RenderFileHandler renderFileHandler; - final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer? - final LodRenderer renderer; - RenderState(IClientLevelWrapper clientLevel) { - this.clientLevel = clientLevel; - renderFileHandler = new RenderFileHandler(dataFileHandler, DhClientServerLevel.this, save.getRenderCacheFolder(serverLevel)); - tree = new LodQuadTree(DhClientServerLevel.this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16, - MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler); - renderBufferHandler = new RenderBufferHandler(tree); - FileScanUtil.scanFile(save, serverLevel, null, renderFileHandler); - renderer = new LodRenderer(renderBufferHandler); - } - CompletableFuture close() { - renderer.close(); - renderBufferHandler.close(); - tree.close(); - return renderFileHandler.flushAndSave(); - } - } - class WorldGenState { - final BatchGenerator batchGenerator; - final GenerationQueue generationQueue; - WorldGenState() { - batchGenerator = new BatchGenerator(DhClientServerLevel.this); - generationQueue = new GenerationQueue(batchGenerator); - dataFileHandler.setGenerationQueue(generationQueue); - } - CompletableFuture close(boolean doInterrupt) { - dataFileHandler.popGenerationQueue(); - return generationQueue.startClosing(true, doInterrupt) - .exceptionally(ex -> { - LOGGER.error("Error closing generation queue", ex); - return null; - }).thenRun(batchGenerator::close) - .exceptionally(ex -> { - LOGGER.error("Error closing world gen", ex); - return null; - }); - } - } - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public final LocalSaveStructure save; - public final GeneratedDataFileHandler dataFileHandler; - public final ChunkToLodBuilder chunkToLodBuilder; - public final IServerLevelWrapper serverLevel; - private final AppliedConfigState generatorEnabled; - public F3Screen.NestedMessage f3Msg; - public final AtomicReference renderState = new AtomicReference<>(); - public final AtomicReference worldGenState = new AtomicReference<>(); - - public DhClientServerLevel(LocalSaveStructure save, IServerLevelWrapper level) { - this.serverLevel = level; - this.save = save; - save.getDataFolder(level).mkdirs(); - save.getRenderCacheFolder(level).mkdirs(); - dataFileHandler = new GeneratedDataFileHandler(this, save.getDataFolder(level)); - FileScanUtil.scanFile(save, serverLevel, dataFileHandler, null); - LOGGER.info("Started DHLevel for {} with saves at {}", level, save); - f3Msg = new F3Screen.NestedMessage(this::f3Log); - chunkToLodBuilder = new ChunkToLodBuilder(); - generatorEnabled = new AppliedConfigState<>(Config.Client.WorldGenerator.enableDistantGeneration); - } - - private String[] f3Log() { - RenderState rs = renderState.get(); - if (rs == null) { - return new String[]{LodUtil.formatLog("level @ {}: Inactive", serverLevel.getDimensionType().getDimensionName())}; - } else { - return new String[]{ - LodUtil.formatLog("level @ {}: Active", serverLevel.getDimensionType().getDimensionName()) - }; - } - } - - @Override - public void clientTick() { - RenderState rs = renderState.get(); - if (rs == null) return; - - if (rs.tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16) { - if (!renderState.compareAndSet(rs, null)) return; //If we fail, we'll just wait for the next tick - IClientLevelWrapper levelWrapper = rs.clientLevel; - rs.close().join(); //TODO: Make it async. - rs = new RenderState(levelWrapper); - if (!renderState.compareAndSet(null, rs)) { //FIXME: How to handle this? - LOGGER.warn("Failed to set render state due to concurrency after changing view distance"); - rs.close(); - return; - } - } - - rs.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); - rs.renderBufferHandler.update(); - } - - private void saveWrites(ChunkSizedData data) { - RenderState rs = renderState.get(); - DhLodPos pos = data.getBBoxLodPos().convertUpwardsTo(FullDataSource.SECTION_SIZE_OFFSET); - if (rs != null) { - rs.renderFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); - } else { - dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); - } - } - - @Override - public void serverTick() { - chunkToLodBuilder.tick(); - } - - public void startRenderer(IClientLevelWrapper clientLevel) { - LOGGER.info("Starting renderer for {}", this); - RenderState rs = new RenderState(clientLevel); - if (!renderState.compareAndSet(null, rs)) { - LOGGER.warn("Failed to start renderer due to concurrency"); - rs.close(); - } else { - generatorEnabled.pollNewValue(); - if (generatorEnabled.get() && worldGenState.get() == null) { - WorldGenState wgs = new WorldGenState(); - if (!worldGenState.compareAndSet(null, wgs)) { - LOGGER.warn("Failed to start world gen due to concurrency"); - wgs.close(false); - } - } - - } - } - - @Override - public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { - RenderState rs = renderState.get(); - if (rs == null) { - LOGGER.error("Tried to call render() on {} when renderer has not been started!", this); - return; - } - rs.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); - } - - public void stopRenderer() { - LOGGER.info("Stopping renderer for {}", this); - RenderState rs = renderState.get(); - if (rs == null) { - LOGGER.warn("Tried to stop renderer for {} when it was not started!", this); - return; - } - while (!renderState.compareAndSet(rs, null)) { - rs = renderState.get(); - if (rs == null) return; - } - rs.close().join(); //TODO: Make it async. - WorldGenState wgs = worldGenState.get(); - if (wgs != null) { - while (!worldGenState.compareAndSet(wgs, null)) { - wgs = worldGenState.get(); - if (wgs == null) return; - } - wgs.close(true).join(); //TODO: Make it async. - } - } - - @Override //FIXME - public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { - IClientLevelWrapper clientLevel = getClientLevelWrapper(); - if (clientLevel == null) { - return 0; - } else { - return clientLevel.computeBaseColor(pos, biome, block); - } - } - - @Override - public IClientLevelWrapper getClientLevelWrapper() { - RenderState rs = renderState.get(); - return rs == null ? null : rs.clientLevel; - } - - @Override - public ILevelWrapper getLevelWrapper() - { - return this.serverLevel; - } - - @Override - public void updateChunk(IChunkWrapper chunk) { - CompletableFuture future = chunkToLodBuilder.tryGenerateData(chunk); - if (future != null) { - future.thenAccept(this::saveWrites); - } - } - - @Override - public void dumpRamUsage() { - //TODO - } - - @Override - public int getMinY() { - return serverLevel.getMinHeight(); - } - - @Override - public CompletableFuture save() { - RenderState rs = renderState.get(); - if (rs != null) { - return rs.renderFileHandler.flushAndSave().thenCombine(dataFileHandler.flushAndSave(), (a, b) -> null); - } else { - return dataFileHandler.flushAndSave(); - } - } - - @Override - public void close() { - RenderState rs = renderState.get(); - if (rs != null) { - while (!renderState.compareAndSet(rs, null)) { - rs = renderState.get(); - if (rs == null) return; - } - rs.close().join(); //TODO: Make it async. - } - WorldGenState wgs = worldGenState.get(); - if (wgs != null) { - while (!worldGenState.compareAndSet(wgs, null)) { - wgs = worldGenState.get(); - if (wgs == null) return; - } - wgs.close(true).join(); //TODO: Make it async. - } - LOGGER.info("Closed {}", this); - } - - - @Override - public void doWorldGen() { - WorldGenState wgs = worldGenState.get(); - if (generatorEnabled.pollNewValue()) { - boolean shouldDoWorldGen = generatorEnabled.get() && renderState.get() != null; - if (shouldDoWorldGen && wgs == null) { - WorldGenState newWgs = new WorldGenState(); - if (!worldGenState.compareAndSet(null, newWgs)) { - LOGGER.warn("Failed to start world gen due to concurrency"); - newWgs.close(false); - } - } else if (!shouldDoWorldGen && wgs != null) { - while (!worldGenState.compareAndSet(wgs, null)) { - wgs = worldGenState.get(); - if (wgs == null) return; - } - wgs.close(true).join(); //TODO: Make it async. - } - } - - if (wgs != null) { - wgs.batchGenerator.update(); - wgs.generationQueue.pollAndStartClosest(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); - } - } - - @Override - public IServerLevelWrapper getServerLevelWrapper() { - return serverLevel; - } + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + public final LocalSaveStructure save; + public final GeneratedDataFileHandler dataFileHandler; + public final ChunkToLodBuilder chunkToLodBuilder; + public final IServerLevelWrapper serverLevel; + private final AppliedConfigState generatorEnabled; + public F3Screen.NestedMessage f3Msg; + public final AtomicReference renderState = new AtomicReference<>(); + public final AtomicReference worldGenState = new AtomicReference<>(); + + + + public DhClientServerLevel(LocalSaveStructure save, IServerLevelWrapper level) + { + this.serverLevel = level; + this.save = save; + save.getDataFolder(level).mkdirs(); + save.getRenderCacheFolder(level).mkdirs(); + this.dataFileHandler = new GeneratedDataFileHandler(this, save.getDataFolder(level)); + FileScanUtil.scanFile(save, this.serverLevel, this.dataFileHandler, null); + LOGGER.info("Started DHLevel for {} with saves at {}", level, save); + this.f3Msg = new F3Screen.NestedMessage(this::f3Log); + this.chunkToLodBuilder = new ChunkToLodBuilder(); + this.generatorEnabled = new AppliedConfigState<>(Config.Client.WorldGenerator.enableDistantGeneration); + } + + + + /** Returns what should be displayed in Minecraft's F3 debug menu */ + private String[] f3Log() + { + RenderState rs = this.renderState.get(); + if (rs == null) + { + return new String[] { LodUtil.formatLog("level @ {}: Inactive", this.serverLevel.getDimensionType().getDimensionName()) }; + } + else + { + return new String[] { + LodUtil.formatLog("level @ {}: Active", this.serverLevel.getDimensionType().getDimensionName()) + }; + } + } + + @Override + public void clientTick() + { + RenderState rs = this.renderState.get(); + if (rs == null) + return; + + if (rs.tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH) + { + if (!this.renderState.compareAndSet(rs, null)) + return; //If we fail, we'll just wait for the next tick + + IClientLevelWrapper levelWrapper = rs.clientLevel; + rs.close().join(); //TODO: Make it async. + rs = new RenderState(levelWrapper); + if (!this.renderState.compareAndSet(null, rs)) + { + //FIXME: How to handle this? + LOGGER.warn("Failed to set render state due to concurrency after changing view distance"); + rs.close(); + return; + } + } + + rs.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + rs.renderBufferHandler.update(); + } + + private void saveWrites(ChunkSizedData data) + { + RenderState rs = this.renderState.get(); + DhLodPos pos = data.getBBoxLodPos().convertUpwardsTo(FullDataSource.SECTION_SIZE_OFFSET); + if (rs != null) + { + rs.renderFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + } + else + { + this.dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + } + } + + @Override + public void serverTick() { this.chunkToLodBuilder.tick(); } + + public void startRenderer(IClientLevelWrapper clientLevel) + { + LOGGER.info("Starting renderer for {}", this); + RenderState rs = new RenderState(clientLevel); + if (!this.renderState.compareAndSet(null, rs)) + { + LOGGER.warn("Failed to start renderer due to concurrency"); + rs.close(); + } + else + { + this.generatorEnabled.pollNewValue(); + if (this.generatorEnabled.get() && this.worldGenState.get() == null) + { + WorldGenState wgs = new WorldGenState(); + if (!this.worldGenState.compareAndSet(null, wgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + wgs.close(false); + } + } + + } + } + + @Override + public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) + { + RenderState rs = this.renderState.get(); + if (rs == null) + { + LOGGER.error("Tried to call render() on {} when renderer has not been started!", this); + return; + } + rs.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); + } + + public void stopRenderer() + { + LOGGER.info("Stopping renderer for {}", this); + RenderState rs = this.renderState.get(); + if (rs == null) + { + LOGGER.warn("Tried to stop renderer for {} when it was not started!", this); + return; + } + + while (!this.renderState.compareAndSet(rs, null)) + { + rs = this.renderState.get(); + if (rs == null) + return; + } + + rs.close().join(); //TODO: Make it async. + WorldGenState wgs = this.worldGenState.get(); + if (wgs != null) + { + while (!this.worldGenState.compareAndSet(wgs, null)) + { + wgs = this.worldGenState.get(); + if (wgs == null) + return; + } + wgs.close(true).join(); //TODO: Make it async. + } + } + + @Override //FIXME + public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) + { + IClientLevelWrapper clientLevel = this.getClientLevelWrapper(); + if (clientLevel == null) + { + return 0; + } + else + { + return clientLevel.computeBaseColor(pos, biome, block); + } + } + + @Override + public IClientLevelWrapper getClientLevelWrapper() + { + RenderState rs = this.renderState.get(); + return rs == null ? null : rs.clientLevel; + } + + @Override + public ILevelWrapper getLevelWrapper() { return this.serverLevel; } + + @Override + public void updateChunk(IChunkWrapper chunk) + { + CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); + if (future != null) + { + future.thenAccept(this::saveWrites); + } + } + + @Override + public void dumpRamUsage() + { + //TODO + } + + @Override + public int getMinY() { return this.serverLevel.getMinHeight(); } + + @Override + public CompletableFuture save() + { + RenderState rs = this.renderState.get(); + if (rs != null) + { + return rs.renderFileHandler.flushAndSave().thenCombine(this.dataFileHandler.flushAndSave(), (a, b) -> null); + } + else + { + return this.dataFileHandler.flushAndSave(); + } + } + + @Override + public void close() + { + RenderState rs = this.renderState.get(); + if (rs != null) + { + while (!this.renderState.compareAndSet(rs, null)) + { + rs = this.renderState.get(); + if (rs == null) + return; + } + rs.close().join(); //TODO: Make it async. + } + + WorldGenState wgs = this.worldGenState.get(); + if (wgs != null) + { + while (!this.worldGenState.compareAndSet(wgs, null)) + { + wgs = this.worldGenState.get(); + if (wgs == null) + return; + } + wgs.close(true).join(); //TODO: Make it async. + } + + LOGGER.info("Closed {}", this); + } + + + @Override + public void doWorldGen() + { + WorldGenState wgs = this.worldGenState.get(); + if (this.generatorEnabled.pollNewValue()) + { + boolean shouldDoWorldGen = this.generatorEnabled.get() && this.renderState.get() != null; + if (shouldDoWorldGen && wgs == null) + { + WorldGenState newWgs = new WorldGenState(); + if (!this.worldGenState.compareAndSet(null, newWgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + newWgs.close(false); + } + } + else if (!shouldDoWorldGen && wgs != null) + { + while (!this.worldGenState.compareAndSet(wgs, null)) + { + wgs = this.worldGenState.get(); + if (wgs == null) + return; + } + wgs.close(true).join(); //TODO: Make it async. + } + } + + if (wgs != null) + { + wgs.batchGenerator.update(); + wgs.generationQueue.pollAndStartClosest(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + } + } + + @Override + public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevel; } + + + + + //================// + // helper classes // + //================// + + class RenderState + { + final IClientLevelWrapper clientLevel; + final LodQuadTree tree; + final RenderFileHandler renderFileHandler; + final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer? + final LodRenderer renderer; + + RenderState(IClientLevelWrapper clientLevel) + { + this.clientLevel = clientLevel; + this.renderFileHandler = new RenderFileHandler(dataFileHandler, DhClientServerLevel.this, save.getRenderCacheFolder(serverLevel)); + this.tree = new LodQuadTree(DhClientServerLevel.this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16, + MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, this.renderFileHandler); + this.renderBufferHandler = new RenderBufferHandler(tree); + FileScanUtil.scanFile(save, serverLevel, null, this.renderFileHandler); + this.renderer = new LodRenderer(this.renderBufferHandler); + } + + CompletableFuture close() + { + this.renderer.close(); + this.renderBufferHandler.close(); + this.tree.close(); + return this.renderFileHandler.flushAndSave(); + } + } + + class WorldGenState + { + final BatchGenerator batchGenerator; + final GenerationQueue generationQueue; + + WorldGenState() + { + this.batchGenerator = new BatchGenerator(DhClientServerLevel.this); + this.generationQueue = new GenerationQueue(this.batchGenerator); + dataFileHandler.setGenerationQueue(this.generationQueue); + } + + CompletableFuture close(boolean doInterrupt) + { + dataFileHandler.popGenerationQueue(); + return this.generationQueue.startClosing(true, doInterrupt) + .exceptionally(ex -> + { + LOGGER.error("Error closing generation queue", ex); + return null; + }).thenRun(this.batchGenerator::close) + .exceptionally(ex -> + { + LOGGER.error("Error closing world gen", ex); + return null; + }); + } + } + } diff --git a/core/src/main/java/com/seibel/lod/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/lod/core/level/DhServerLevel.java index f6c904fa6..35bf59255 100644 --- a/core/src/main/java/com/seibel/lod/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/DhServerLevel.java @@ -13,63 +13,62 @@ import java.util.concurrent.CompletableFuture; public class DhServerLevel implements IDhServerLevel { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - - public final LocalSaveStructure save; - public final DataFileHandler dataFileHandler; - public final IServerLevelWrapper level; - - public DhServerLevel(LocalSaveStructure save, IServerLevelWrapper level) { - this.save = save; - this.level = level; - save.getDataFolder(level).mkdirs(); - dataFileHandler = new DataFileHandler(this, save.getDataFolder(level)); //FIXME: GenerationQueue - FileScanUtil.scanFile(save, level, dataFileHandler, null); - LOGGER.info("Started DHLevel for {} with saves at {}", level, save); - } - - public void serverTick() { - //Nothing for now - } - - @Override - public int getMinY() { - return level.getMinHeight(); - } - - @Override - public void dumpRamUsage() { - //TODO - } - @Override - public void close() { - dataFileHandler.close(); - LOGGER.info("Closed DHLevel for {}", level); - } - @Override - public CompletableFuture save() { - return dataFileHandler.flushAndSave(); - } - - @Override - public void doWorldGen() { - // FIXME: No world gen for server side only for now - } - - @Override - public IServerLevelWrapper getServerLevelWrapper() { - return level; - } - - @Override - public ILevelWrapper getLevelWrapper() - { - return this.level; - } - - @Override - public void updateChunk(IChunkWrapper chunk) { - //TODO - } - + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + public final LocalSaveStructure save; + public final DataFileHandler dataFileHandler; + public final IServerLevelWrapper level; + + public DhServerLevel(LocalSaveStructure save, IServerLevelWrapper level) + { + this.save = save; + this.level = level; + save.getDataFolder(level).mkdirs(); + this.dataFileHandler = new DataFileHandler(this, save.getDataFolder(level)); //FIXME: GenerationQueue + FileScanUtil.scanFile(save, level, this.dataFileHandler, null); + LOGGER.info("Started DHLevel for {} with saves at {}", level, save); + } + + public void serverTick() + { + //Nothing for now + } + + @Override + public int getMinY() { return this.level.getMinHeight(); } + + @Override + public void dumpRamUsage() + { + //TODO + } + + @Override + public void close() + { + this.dataFileHandler.close(); + LOGGER.info("Closed DHLevel for {}", this.level); + } + + @Override + public CompletableFuture save() { return this.dataFileHandler.flushAndSave(); } + + @Override + public void doWorldGen() + { + // FIXME: No world gen for server side only for now + } + + @Override + public IServerLevelWrapper getServerLevelWrapper() { return this.level; } + + @Override + public ILevelWrapper getLevelWrapper() { return this.level; } + + @Override + public void updateChunk(IChunkWrapper chunk) + { + //TODO + } + }