From f91629bf21af60a21fa4208c3859fe0dfcb70e57 Mon Sep 17 00:00:00 2001 From: TomTheFurry Date: Sun, 18 Sep 2022 12:55:26 +0800 Subject: [PATCH 1/2] Make it build again --- .../lod/api/interfaces/config/client/IDhApiGraphicsConfig.java | 1 - .../api/interfaces/config/client/IDhApiGraphicsFogConfig.java | 1 - 2 files changed, 2 deletions(-) diff --git a/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsConfig.java b/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsConfig.java index 52b746ff7..a8cb99fa5 100644 --- a/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsConfig.java +++ b/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsConfig.java @@ -22,7 +22,6 @@ package com.seibel.lod.api.interfaces.config.client; import com.seibel.lod.api.enums.config.*; import com.seibel.lod.api.enums.rendering.ERendererMode; import com.seibel.lod.api.interfaces.config.IDhApiConfigValue; -import com.seibel.lod.api.items.enums.config.*; import com.seibel.lod.api.interfaces.config.IDhApiConfigGroup; /** diff --git a/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsFogConfig.java b/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsFogConfig.java index 3802a617b..37035826d 100644 --- a/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsFogConfig.java +++ b/api/src/main/java/com/seibel/lod/api/interfaces/config/client/IDhApiGraphicsFogConfig.java @@ -22,7 +22,6 @@ package com.seibel.lod.api.interfaces.config.client; import com.seibel.lod.api.enums.rendering.*; import com.seibel.lod.api.interfaces.config.IDhApiConfigGroup; import com.seibel.lod.api.interfaces.config.IDhApiConfigValue; -import com.seibel.lod.api.items.enums.rendering.*; /** * Distant Horizons' fog configuration.

From 2e773279b7dc2f5eace7c82a0b9c559c698c3ab1 Mon Sep 17 00:00:00 2001 From: TomTheFurry Date: Sun, 18 Sep 2022 13:58:02 +0800 Subject: [PATCH 2/2] Rework the Level data member organization and make world gen enable/disable based on settings --- .../lod/core/config/AppliedConfigState.java | 27 ++ .../seibel/lod/core/level/DhClientLevel.java | 8 +- .../lod/core/level/DhClientServerLevel.java | 276 ++++++++++-------- .../seibel/lod/core/level/IClientLevel.java | 2 - .../lod/core/render/renderer/LodRenderer.java | 9 +- 5 files changed, 184 insertions(+), 138 deletions(-) create mode 100644 core/src/main/java/com/seibel/lod/core/config/AppliedConfigState.java diff --git a/core/src/main/java/com/seibel/lod/core/config/AppliedConfigState.java b/core/src/main/java/com/seibel/lod/core/config/AppliedConfigState.java new file mode 100644 index 000000000..9f1a9caf8 --- /dev/null +++ b/core/src/main/java/com/seibel/lod/core/config/AppliedConfigState.java @@ -0,0 +1,27 @@ +package com.seibel.lod.core.config; + +import com.seibel.lod.core.config.types.ConfigEntry; + +// TODO: Make this intergrate with the config system +public class AppliedConfigState { + final ConfigEntry entry; + T activeValue; + + public AppliedConfigState(ConfigEntry entryToWatch) { + this.entry = entryToWatch; + activeValue = entryToWatch.get(); + } + + public boolean pollNewValue() { + T newValue = entry.get(); + if (newValue.equals(activeValue)) { + return false; + } + activeValue = newValue; + return true; + } + + public T get() { + return activeValue; + } +} 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 200a12f55..b7ac47368 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 @@ -58,22 +58,16 @@ public class DhClientLevel implements IClientLevel { public void clientTick() { tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); renderBufferHandler.update(); - return; } @Override public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { if (renderer == null) { - renderer = new LodRenderer(this); + renderer = new LodRenderer(renderBufferHandler); } renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); } - @Override - public RenderBufferHandler getRenderBufferHandler() { - return renderBufferHandler; - } - @Override public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return 0; //TODO 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 799e98b9a..d33f0a2b3 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 @@ -1,5 +1,6 @@ package com.seibel.lod.core.level; +import com.seibel.lod.core.config.AppliedConfigState; import com.seibel.lod.core.datatype.full.ChunkSizedData; import com.seibel.lod.core.datatype.full.FullDataSource; import com.seibel.lod.core.datatype.transform.ChunkToLodBuilder; @@ -33,26 +34,62 @@ import com.seibel.lod.core.wrapperInterfaces.world.IServerLevelWrapper; import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.atomic.AtomicReference; public class DhClientServerLevel implements IClientLevel, IServerLevel { + 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 volatile GenerationQueue generationQueue = null; - public RenderFileHandler renderFileHandler = null; - public RenderBufferHandler renderBufferHandler = null; //TODO: Should this be owned by renderer? public final IServerLevelWrapper serverLevel; - public IClientLevelWrapper clientLevel; - public LodRenderer renderer = null; - public LodQuadTree tree = null; - public volatile BatchGenerator worldGenerator = null; - private final ReentrantReadWriteLock renderStateLifecycleLock = new ReentrantReadWriteLock(); - + 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; @@ -64,10 +101,12 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel { 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() { - if (clientLevel == null) { + RenderState rs = renderState.get(); + if (rs == null) { return new String[]{LodUtil.formatLog("level @ {}: Inactive", serverLevel.getDimensionType().getDimensionName())}; } else { return new String[]{ @@ -78,35 +117,30 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel { @Override public void clientTick() { - //LOGGER.info("Client tick for {}", level); - renderStateLifecycleLock.readLock().lock(); - try { - if (clientLevel == null) return; - if (tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16) { - IClientLevelWrapper temp = clientLevel; - renderStateLifecycleLock.readLock().unlock(); - renderStateLifecycleLock.writeLock().lock(); - try { - stopRenderer(); - startRenderer(temp); - } finally { - renderStateLifecycleLock.readLock().lock(); - renderStateLifecycleLock.writeLock().unlock(); - } + 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; } - tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); - renderBufferHandler.update(); - } finally { - renderStateLifecycleLock.readLock().unlock(); } + + rs.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + rs.renderBufferHandler.update(); } private void saveWrites(ChunkSizedData data) { - RenderFileHandler renderFileHandler = this.renderFileHandler; + RenderState rs = renderState.get(); DhLodPos pos = data.getBBoxLodPos().convertUpwardsTo(FullDataSource.SECTION_SIZE_OFFSET); - if (renderFileHandler != null) { - renderFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data); + if (rs != null) { + rs.renderFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data); } else { dataFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data); } @@ -119,93 +153,69 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel { public void startRenderer(IClientLevelWrapper clientLevel) { LOGGER.info("Starting renderer for {}", this); - renderStateLifecycleLock.writeLock().lock(); - try { - if (renderBufferHandler != null || this.clientLevel != null) { - LOGGER.warn("Tried to call startRenderer() on {} when renderer is already setup!", this); - return; + 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); + } } - this.clientLevel = clientLevel; - // TODO: Make a registry for generators for modding support. - worldGenerator = new BatchGenerator(this); - generationQueue = new GenerationQueue(worldGenerator); - dataFileHandler.setGenerationQueue(generationQueue); - renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(serverLevel)); - tree = new LodQuadTree(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); - } finally { - renderStateLifecycleLock.writeLock().unlock(); + } } @Override public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { - if (!renderStateLifecycleLock.readLock().tryLock()) return; - try { - if (renderBufferHandler == null) { - LOGGER.error("Tried to call render() on {} when renderer has not been started!", this); - return; - } - if (renderer == null) { - renderer = new LodRenderer(this); - } - renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); - } finally { - renderStateLifecycleLock.readLock().unlock(); + 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); - renderStateLifecycleLock.writeLock().lock(); - try { - if (renderBufferHandler == null) { - LOGGER.warn("Tried to call stopRenderer() on {} when renderer is already closed!", this); - return; + 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. + } + } - tree.close(); - tree = null; - dataFileHandler.popGenerationQueue(); - final BatchGenerator f_worldGen = worldGenerator; - CompletableFuture closer = generationQueue.startClosing(true, true) - .exceptionally(ex -> { - LOGGER.error("Error closing generation queue", ex); - return null; - }).thenRun(f_worldGen::close) - .exceptionally(ex -> { - LOGGER.error("Error closing world gen", ex); - return null; - }); - generationQueue = null; - worldGenerator = null; - renderBufferHandler.close(); - renderBufferHandler = null; - renderFileHandler.flushAndSave(); //Ignore the completion feature so that this action is async - renderFileHandler.close(); - renderFileHandler = null; - closer.join(); // TODO: Could this cause deadlocks? we are blocking in main thread. - clientLevel = null; - } finally { - renderStateLifecycleLock.writeLock().unlock(); + @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 RenderBufferHandler getRenderBufferHandler() { - return renderBufferHandler; - } - - @Override - public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { - return clientLevel.computeBaseColor(pos, biome, block); - } - @Override public IClientLevelWrapper getClientLevelWrapper() { - return clientLevel; + RenderState rs = renderState.get(); + return rs == null ? null : rs.clientLevel; } @Override @@ -234,26 +244,31 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel { @Override public CompletableFuture save() { - - if (renderFileHandler != null) { - return renderFileHandler.flushAndSave().thenCompose(v -> dataFileHandler.flushAndSave()); + 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() { - renderStateLifecycleLock.writeLock().lock(); - try { - if (generationQueue != null) generationQueue.close(); - if (worldGenerator != null) worldGenerator.close(); - if (renderer != null) renderer.close(); - if (tree != null) tree.close(); - if (renderBufferHandler != null) renderBufferHandler.close(); - if (renderFileHandler != null) renderFileHandler.close(); - dataFileHandler.close(); - } finally { - renderStateLifecycleLock.writeLock().unlock(); + 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); } @@ -261,12 +276,27 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel { @Override public void doWorldGen() { - final BatchGenerator f_worldGen = worldGenerator; - if (f_worldGen != null) { - f_worldGen.update(); - final GenerationQueue f_genQueue = generationQueue; - if (f_genQueue != null) - f_genQueue.pollAndStartClosest(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + 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())); } } diff --git a/core/src/main/java/com/seibel/lod/core/level/IClientLevel.java b/core/src/main/java/com/seibel/lod/core/level/IClientLevel.java index afafbed7a..7222b4815 100644 --- a/core/src/main/java/com/seibel/lod/core/level/IClientLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/IClientLevel.java @@ -16,8 +16,6 @@ public interface IClientLevel extends ILevel { void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler); - RenderBufferHandler getRenderBufferHandler(); - int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block); IClientLevelWrapper getClientLevelWrapper(); diff --git a/core/src/main/java/com/seibel/lod/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/lod/core/render/renderer/LodRenderer.java index 1f5e595b9..1c1943747 100644 --- a/core/src/main/java/com/seibel/lod/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/lod/core/render/renderer/LodRenderer.java @@ -106,16 +106,16 @@ public class LodRenderer private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); public EDebugMode previousDebugMode = null; - public final IClientLevel level; + public final RenderBufferHandler bufferHandler; // The shader program LodRenderProgram shaderProgram = null; public QuadElementBuffer quadIBO = null; public boolean isSetupComplete = false; - public LodRenderer(IClientLevel level) + public LodRenderer(RenderBufferHandler bufferHandler) { - this.level = level; + this.bufferHandler = bufferHandler; } private boolean closeCalled = false; @@ -147,9 +147,6 @@ public class LodRenderer GLProxy glProxy = GLProxy.getInstance(); if (Config.Client.Graphics.FogQuality.disableVanillaFog.get()) MC_RENDER.tryDisableVanillaFog(); - - // The Buffer manager - RenderBufferHandler bufferHandler = level.getRenderBufferHandler(); //===================// // draw params setup //