From 381d3fe21654ae39bed82683a0a0420bb8e914f2 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 25 Feb 2023 21:31:18 -0600 Subject: [PATCH] Move shared Dh(Server)ClientLevel code into AbstractDhClientLevel This fixes DhServerClientLevel's crashing --- .../lod/core/api/internal/ClientApi.java | 29 +- .../lod/core/api/internal/SharedApi.java | 2 - .../lod/core/level/AbstractDhClientLevel.java | 277 ++++++++++++++++++ .../seibel/lod/core/level/DhClientLevel.java | 201 +------------ .../lod/core/level/DhClientServerLevel.java | 16 +- .../com/seibel/lod/core/level/IDhLevel.java | 5 +- .../core/level/states/ClientRenderState.java | 20 +- .../minecraft/IMinecraftClientWrapper.java | 1 + 8 files changed, 329 insertions(+), 222 deletions(-) create mode 100644 core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java diff --git a/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java index 2043a460d..ce863b329 100644 --- a/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java @@ -96,26 +96,33 @@ public class ClientApi public void onClientOnlyConnected() { - if (ENABLE_EVENT_LOGGING) + // only continue if the client is connected to a different server + if (MC.clientConnectedToDedicatedServer()) { - LOGGER.info("Client on ClientOnly mode connecting."); + if (ENABLE_EVENT_LOGGING) + { + LOGGER.info("Client on ClientOnly mode connecting."); + } + + SharedApi.setDhWorld(new DhClientWorld()); } - - SharedApi.setDhWorld(new DhClientWorld()); } public void onClientOnlyDisconnected() { - AbstractDhWorld world = SharedApi.getAbstractDhWorld(); - if (world != null) + if (MC.clientConnectedToDedicatedServer()) { - if (ENABLE_EVENT_LOGGING) + AbstractDhWorld world = SharedApi.getAbstractDhWorld(); + if (world != null) { - LOGGER.info("Client on ClientOnly mode disconnecting."); + if (ENABLE_EVENT_LOGGING) + { + LOGGER.info("Client on ClientOnly mode disconnecting."); + } + + world.close(); + SharedApi.setDhWorld(null); } - - world.close(); - SharedApi.setDhWorld(null); } } diff --git a/core/src/main/java/com/seibel/lod/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/lod/core/api/internal/SharedApi.java index 2967e07eb..7905a3459 100644 --- a/core/src/main/java/com/seibel/lod/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/lod/core/api/internal/SharedApi.java @@ -22,10 +22,8 @@ public class SharedApi public static void setDhWorld(AbstractDhWorld newWorld) { currentWorld = newWorld; } public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; } - /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */ public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld != null && DhClientServerWorld.class.isInstance(currentWorld)) ? (DhClientServerWorld) currentWorld : null; } - /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientWorld} or {@link DhClientServerWorld} */ public static IDhClientWorld getIDhClientWorld() { return (currentWorld != null && IDhClientWorld.class.isInstance(currentWorld)) ? (IDhClientWorld) currentWorld : null; } /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhServerWorld} or {@link DhClientServerWorld} */ diff --git a/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java b/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java new file mode 100644 index 000000000..7234a1558 --- /dev/null +++ b/core/src/main/java/com/seibel/lod/core/level/AbstractDhClientLevel.java @@ -0,0 +1,277 @@ +package com.seibel.lod.core.level; + +import com.seibel.lod.core.config.Config; +import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource; +import com.seibel.lod.core.dataObjects.fullData.sources.FullDataSource; +import com.seibel.lod.core.dataObjects.transformers.ChunkToLodBuilder; +import com.seibel.lod.core.dependencyInjection.SingletonInjector; +import com.seibel.lod.core.file.fullDatafile.FullDataFileHandler; +import com.seibel.lod.core.file.fullDatafile.IFullDataSourceProvider; +import com.seibel.lod.core.file.fullDatafile.RemoteFullDataFileHandler; +import com.seibel.lod.core.file.structure.AbstractSaveStructure; +import com.seibel.lod.core.level.states.ClientRenderState; +import com.seibel.lod.core.logging.DhLoggerBuilder; +import com.seibel.lod.core.logging.f3.F3Screen; +import com.seibel.lod.core.pos.DhBlockPos2D; +import com.seibel.lod.core.pos.DhLodPos; +import com.seibel.lod.core.pos.DhSectionPos; +import com.seibel.lod.core.util.FileScanUtil; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.util.math.Mat4f; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +/** + * This contains code that is shared between {@link DhClientLevel} {@link DhClientServerLevel} + */ +public abstract class AbstractDhClientLevel implements IDhClientLevel +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + + public final AbstractSaveStructure saveStructure; + public final ChunkToLodBuilder chunkToLodBuilder; + + public FullDataFileHandler fullDataFileHandler; + + public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); + + + + //=============// + // constructor // + //=============// + + public AbstractDhClientLevel(AbstractSaveStructure saveStructure, ILevelWrapper levelWrapper) + { + this.saveStructure = saveStructure; + if (this.saveStructure.getFullDataFolder(levelWrapper).mkdirs()) + { + LOGGER.warn("unable to create full data folder."); + } + if (this.saveStructure.getRenderCacheFolder(levelWrapper).mkdirs()) + { + LOGGER.warn("unable to create cache folder."); + } + + this.fullDataFileHandler = new RemoteFullDataFileHandler(this, this.saveStructure.getFullDataFolder(levelWrapper)); + FileScanUtil.scanFiles(saveStructure, levelWrapper, this.fullDataFileHandler, null); + + this.chunkToLodBuilder = new ChunkToLodBuilder(); + } + + + + //==============// + // tick methods // + //==============// + + /** + * Includes logic used by both {@link DhClientServerLevel} and {@link DhClientServerLevel} + * @return whether the tick method completed + */ + protected boolean baseClientTick() + { + ClientRenderState clientRenderState = this.ClientRenderStateRef.get(); + if (clientRenderState == null) + { + return false; + } + + if (clientRenderState.quadtree.blockRenderDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH) + { + if (!this.ClientRenderStateRef.compareAndSet(clientRenderState, null)) + { + return false; //If we fail, we'll just wait for the next tick + } + + clientRenderState.closeAsync().join(); //TODO: Make it async. + clientRenderState = new ClientRenderState(this, this.fullDataFileHandler, this.saveStructure); + if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState)) + { + //FIXME: How to handle this? + LOGGER.warn("Failed to set render state due to concurrency after changing view distance"); + clientRenderState.closeAsync(); + return false; + } + } + + clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + clientRenderState.renderer.bufferHandler.update(); + + return true; + } + + + + //========// + // render // + //========// + + /** @return if the {@link ClientRenderState} was successfully swapped */ + protected boolean setAndStartRenderer() + { + ClientRenderState ClientRenderState = new ClientRenderState(this, this.fullDataFileHandler, this.saveStructure); + if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState)) + { + LOGGER.warn("Failed to start renderer due to concurrency"); + ClientRenderState.closeAsync(); + return false; + } + else + { + return true; + } + } + + @Override + public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) + { + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState == null) + { + LOGGER.error("Tried to call render() on "+this+" when renderer has not been started!"); + return; + } + ClientRenderState.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); + } + + public void stopRenderer() + { + LOGGER.info("Stopping renderer for "+this); + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState == null) + { + LOGGER.warn("Tried to stop renderer for "+this+" when it was not started!"); + return; + } + + // stop the render state + while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null)) // TODO why is there a while loop here? + { + ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState == null) + { + return; + } + } + ClientRenderState.closeAsync().join(); //TODO: Make it async. + } + + + + //===============// + // data handling // + //===============// + + @Override + public void updateChunkAsync(IChunkWrapper chunk) + { + CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); + if (future != null) + { + future.thenAccept(this::saveWrites); + } + } + private void saveWrites(ChunkSizedFullDataSource data) + { + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + DhLodPos pos = data.getBBoxLodPos().convertToDetailLevel(FullDataSource.SECTION_SIZE_OFFSET); + if (ClientRenderState != null) + { + ClientRenderState.renderSourceFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + } + else + { + this.fullDataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + } + } + + @Override + public CompletableFuture saveAsync() + { + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState != null) + { + return ClientRenderState.renderSourceFileHandler.flushAndSave().thenCombine(this.fullDataFileHandler.flushAndSave(), (voidA, voidB) -> null); + } + else + { + return this.fullDataFileHandler.flushAndSave(); + } + } + + + + /** Includes logic used by both {@link DhClientServerLevel} and {@link DhClientServerLevel} */ + protected void baseClose() + { + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState != null) + { + // TODO does this have to be in a while loop, if so why? + while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null)) + { + ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState == null) + { + break; + } + } + + if (ClientRenderState != null) + { + ClientRenderState.closeAsync().join(); //TODO: Make this async. + } + } + } + + + + + //=======================// + // misc helper functions // + //=======================// + + @Override + public void dumpRamUsage() + { + //TODO + } + + /** Returns what should be displayed in Minecraft's F3 debug menu */ + protected String[] f3Log() + { + String dimName = this.getLevelWrapper().getDimensionType().getDimensionName(); + ClientRenderState renderState = this.ClientRenderStateRef.get(); + if (renderState == null) + { + return new String[] { "level @ "+dimName+": Inactive" }; + } + else + { + return new String[] { "level @ "+dimName+": Active" }; + } + } + + @Override + public IFullDataSourceProvider getFileHandler() { return this.fullDataFileHandler; } + + @Override + public void clearRenderDataCache() + { + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); + if (ClientRenderState != null && ClientRenderState.quadtree != null) + { + ClientRenderState.quadtree.clearRenderDataCache(); + } + } + +} 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 9362a4712..ed14f681c 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 @@ -12,8 +12,6 @@ import com.seibel.lod.core.pos.DhLodPos; import com.seibel.lod.core.pos.DhSectionPos; import com.seibel.lod.core.util.FileScanUtil; import com.seibel.lod.core.file.fullDatafile.RemoteFullDataFileHandler; -import com.seibel.lod.core.pos.DhBlockPos2D; -import com.seibel.lod.core.config.Config; import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.pos.DhBlockPos; @@ -31,17 +29,16 @@ import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; -public class DhClientLevel implements IDhClientLevel +/** The level used when connected to a server */ +public class DhClientLevel extends AbstractDhClientLevel implements IDhClientLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public final IClientLevelWrapper clientLevelWrapper; - - public final AbstractSaveStructure saveStructure; - public final ChunkToLodBuilder chunkToLodBuilder; + private final IClientLevelWrapper clientLevelWrapper; public final F3Screen.NestedMessage f3Message; + public FullDataFileHandler fullDataFileHandler; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); @@ -54,34 +51,10 @@ public class DhClientLevel implements IDhClientLevel public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper) { - this(saveStructure, clientLevelWrapper, null); + super(saveStructure, clientLevelWrapper); - this.fullDataFileHandler = new RemoteFullDataFileHandler(this, this.saveStructure.getFullDataFolder(this.clientLevelWrapper)); - FileScanUtil.scanFiles(saveStructure, this.clientLevelWrapper, this.fullDataFileHandler, null); - } - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, FullDataFileHandler fullDataFileHandler) - { this.clientLevelWrapper = clientLevelWrapper; - - this.saveStructure = saveStructure; - if (this.saveStructure.getFullDataFolder(this.clientLevelWrapper).mkdirs()) - { - LOGGER.warn("unable to create full data folder."); - } - if (this.saveStructure.getRenderCacheFolder(this.clientLevelWrapper).mkdirs()) - { - LOGGER.warn("unable to create cache folder."); - } - - // TODO not a great way of handling this, but it works for now - if (fullDataFileHandler != null) - { - this.fullDataFileHandler = fullDataFileHandler; - FileScanUtil.scanFiles(saveStructure, this.clientLevelWrapper, this.fullDataFileHandler, null); - } - - this.f3Message = new F3Screen.NestedMessage(this::f3Log); - this.chunkToLodBuilder = new ChunkToLodBuilder(); + this.f3Message = new F3Screen.NestedMessage(super::f3Log); LOGGER.info("Started DHLevel for "+this.clientLevelWrapper+" with saves at "+this.saveStructure); @@ -103,42 +76,6 @@ public class DhClientLevel implements IDhClientLevel this.chunkToLodBuilder.tick(); } - /** - * Includes logic used by both {@link DhClientServerLevel} and {@link DhClientServerLevel} - * @return whether the tick method completed - */ - protected boolean baseClientTick() - { - ClientRenderState clientRenderState = this.ClientRenderStateRef.get(); - if (clientRenderState == null) - { - return false; - } - - if (clientRenderState.quadtree.blockRenderDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH) - { - if (!this.ClientRenderStateRef.compareAndSet(clientRenderState, null)) - { - return false; //If we fail, we'll just wait for the next tick - } - - IClientLevelWrapper levelWrapper = clientRenderState.clientLevel; - clientRenderState.closeAsync().join(); //TODO: Make it async. - clientRenderState = new ClientRenderState(this, levelWrapper); - if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState)) - { - //FIXME: How to handle this? - LOGGER.warn("Failed to set render state due to concurrency after changing view distance"); - clientRenderState.closeAsync(); - return false; - } - } - - clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); - clientRenderState.renderer.bufferHandler.update(); - - return true; - } @@ -151,55 +88,6 @@ public class DhClientLevel implements IDhClientLevel LOGGER.info("Starting renderer for "+this); this.setAndStartRenderer(); } - /** @return if the {@link ClientRenderState} was successfully swapped */ - protected boolean setAndStartRenderer() - { - ClientRenderState ClientRenderState = new ClientRenderState(this, this.clientLevelWrapper); - if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState)) - { - LOGGER.warn("Failed to start renderer due to concurrency"); - ClientRenderState.closeAsync(); - return false; - } - else - { - return true; - } - } - - @Override - public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) - { - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState == null) - { - LOGGER.error("Tried to call render() on "+this+" when renderer has not been started!"); - return; - } - ClientRenderState.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); - } - - public void stopRenderer() - { - LOGGER.info("Stopping renderer for "+this); - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState == null) - { - LOGGER.warn("Tried to stop renderer for "+this+" when it was not started!"); - return; - } - - // stop the render state - while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null)) // TODO why is there a while loop here? - { - ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState == null) - { - return; - } - } - ClientRenderState.closeAsync().join(); //TODO: Make it async. - } @@ -224,43 +112,6 @@ public class DhClientLevel implements IDhClientLevel // data handling // //===============// - @Override - public void updateChunkAsync(IChunkWrapper chunk) - { - CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); - if (future != null) - { - future.thenAccept(this::saveWrites); - } - } - private void saveWrites(ChunkSizedFullDataSource data) - { - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); - DhLodPos pos = data.getBBoxLodPos().convertToDetailLevel(FullDataSource.SECTION_SIZE_OFFSET); - if (ClientRenderState != null) - { - ClientRenderState.renderSourceFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); - } - else - { - this.fullDataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); - } - } - - @Override - public CompletableFuture saveAsync() - { - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState != null) - { - return ClientRenderState.renderSourceFileHandler.flushAndSave().thenCombine(this.fullDataFileHandler.flushAndSave(), (voidA, voidB) -> null); - } - else - { - return this.fullDataFileHandler.flushAndSave(); - } - } - @Override @@ -269,28 +120,6 @@ public class DhClientLevel implements IDhClientLevel this.baseClose(); LOGGER.info("Closed "+DhClientLevel.class.getSimpleName()+" for "+this.clientLevelWrapper); } - /** Includes logic used by both {@link DhClientServerLevel} and {@link DhClientServerLevel} */ - protected void baseClose() - { - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState != null) - { - // TODO does this have to be in a while loop, if so why? - while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null)) - { - ClientRenderState = this.ClientRenderStateRef.get(); - if (ClientRenderState == null) - { - break; - } - } - - if (ClientRenderState != null) - { - ClientRenderState.closeAsync().join(); //TODO: Make this async. - } - } - } @@ -299,22 +128,6 @@ public class DhClientLevel implements IDhClientLevel // misc helper functions // //=======================// - /** Returns what should be displayed in Minecraft's F3 debug menu */ - protected String[] f3Log() - { - ClientRenderState renderState = this.ClientRenderStateRef.get(); - if (renderState == null) - { - return new String[] { LodUtil.formatLog("level @ {}: Inactive", this.clientLevelWrapper.getDimensionType().getDimensionName()) }; - } - else - { - return new String[] { - LodUtil.formatLog("level @ {}: Active", this.clientLevelWrapper.getDimensionType().getDimensionName()) - }; - } - } - @Override public void dumpRamUsage() { @@ -334,6 +147,4 @@ public class DhClientLevel implements IDhClientLevel } } - - } 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 1551b2ae6..4a8604f26 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 @@ -9,11 +9,14 @@ import com.seibel.lod.core.file.structure.AbstractSaveStructure; import com.seibel.lod.core.generation.BatchGenerator; import com.seibel.lod.core.generation.WorldGenerationQueue; import com.seibel.lod.core.file.fullDatafile.GeneratedFullDataFileHandler; +import com.seibel.lod.core.level.states.ClientRenderState; +import com.seibel.lod.core.logging.f3.F3Screen; import com.seibel.lod.core.util.FileScanUtil; import com.seibel.lod.core.pos.DhBlockPos2D; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.pos.DhBlockPos; +import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; @@ -26,15 +29,16 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; /** The level used on a singleplayer world */ -public class DhClientServerLevel extends DhClientLevel implements IDhClientLevel, IDhServerLevel +public class DhClientServerLevel extends AbstractDhClientLevel implements IDhClientLevel, IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); public final IServerLevelWrapper serverLevelWrapper; + public final F3Screen.NestedMessage f3Message; /** - * This is separate from {@link DhClientLevel#fullDataFileHandler} and included + * This is separate from {@link AbstractDhClientLevel#fullDataFileHandler} * since the base {@link FullDataFileHandler} doesn't support world generation */ public final GeneratedFullDataFileHandler generatedFullDataFileHandler; @@ -46,12 +50,14 @@ public class DhClientServerLevel extends DhClientLevel implements IDhClientLevel public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper) { - super(saveStructure, serverLevelWrapper.tryGetClientLevelWrapper(), null); + super(saveStructure, serverLevelWrapper); this.serverLevelWrapper = serverLevelWrapper; + this.f3Message = new F3Screen.NestedMessage(super::f3Log); this.generatedFullDataFileHandler = new GeneratedFullDataFileHandler(this, saveStructure.getFullDataFolder(serverLevelWrapper)); this.fullDataFileHandler = this.generatedFullDataFileHandler; + FileScanUtil.scanFiles(saveStructure, this.serverLevelWrapper, this.fullDataFileHandler, null); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.WorldGenerator.enableDistantGeneration); @@ -174,7 +180,7 @@ public class DhClientServerLevel extends DhClientLevel implements IDhClientLevel // level handling // //================// - @Override //FIXME this can fail if the clientLevel hasn't been created yet + @Override //FIXME this can fail if the clientLevel isn't available yet, maybe in that case we could return -1 and handle it upstream? public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { IClientLevelWrapper clientLevel = this.getClientLevelWrapper(); @@ -188,6 +194,8 @@ public class DhClientServerLevel extends DhClientLevel implements IDhClientLevel } } + @Override + public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); } @Override public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevelWrapper; } @Override diff --git a/core/src/main/java/com/seibel/lod/core/level/IDhLevel.java b/core/src/main/java/com/seibel/lod/core/level/IDhLevel.java index 15fbf4ff1..a5ffcaa57 100644 --- a/core/src/main/java/com/seibel/lod/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/IDhLevel.java @@ -13,7 +13,10 @@ public interface IDhLevel extends AutoCloseable void dumpRamUsage(); - /** May return either a client or server level wrapper. */ + /** + * May return either a client or server level wrapper.
+ * Should not return null + */ ILevelWrapper getLevelWrapper(); void updateChunkAsync(IChunkWrapper chunk); diff --git a/core/src/main/java/com/seibel/lod/core/level/states/ClientRenderState.java b/core/src/main/java/com/seibel/lod/core/level/states/ClientRenderState.java index 9026dbe7c..080f0e5c2 100644 --- a/core/src/main/java/com/seibel/lod/core/level/states/ClientRenderState.java +++ b/core/src/main/java/com/seibel/lod/core/level/states/ClientRenderState.java @@ -2,9 +2,10 @@ package com.seibel.lod.core.level.states; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.dependencyInjection.SingletonInjector; +import com.seibel.lod.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.lod.core.file.renderfile.RenderSourceFileHandler; -import com.seibel.lod.core.level.DhClientLevel; -import com.seibel.lod.core.level.DhClientServerLevel; +import com.seibel.lod.core.file.structure.AbstractSaveStructure; +import com.seibel.lod.core.level.IDhClientLevel; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.render.LodQuadTree; import com.seibel.lod.core.render.RenderBufferHandler; @@ -12,7 +13,7 @@ import com.seibel.lod.core.render.renderer.LodRenderer; import com.seibel.lod.core.util.FileScanUtil; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; @@ -22,23 +23,24 @@ public class ClientRenderState private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public final IClientLevelWrapper clientLevel; + public final ILevelWrapper levelWrapper; public final LodQuadTree quadtree; public final RenderSourceFileHandler renderSourceFileHandler; public final LodRenderer renderer; - public ClientRenderState(DhClientLevel parent, IClientLevelWrapper clientLevel) + public ClientRenderState(IDhClientLevel dhClientLevel, IFullDataSourceProvider fullDataSourceProvider, + AbstractSaveStructure saveStructure) { - this.clientLevel = clientLevel; - this.renderSourceFileHandler = new RenderSourceFileHandler(parent.fullDataFileHandler, parent, parent.saveStructure.getRenderCacheFolder(parent.getLevelWrapper())); + this.levelWrapper = dhClientLevel.getLevelWrapper(); + this.renderSourceFileHandler = new RenderSourceFileHandler(fullDataSourceProvider, dhClientLevel, saveStructure.getFullDataFolder(this.levelWrapper)); - this.quadtree = new LodQuadTree(parent, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH, + this.quadtree = new LodQuadTree(dhClientLevel, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH, MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, this.renderSourceFileHandler); RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree); - FileScanUtil.scanFiles(parent.saveStructure, parent.getLevelWrapper(), null, this.renderSourceFileHandler); + FileScanUtil.scanFiles(saveStructure, this.levelWrapper, fullDataSourceProvider, this.renderSourceFileHandler); this.renderer = new LodRenderer(renderBufferHandler); } diff --git a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java index d139b8a7f..a13297d27 100644 --- a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java +++ b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java @@ -59,6 +59,7 @@ public interface IMinecraftClientWrapper extends IBindable float getShade(ELodDirection lodDirection); boolean hasSinglePlayerServer(); + boolean clientConnectedToDedicatedServer(); String getCurrentServerName(); String getCurrentServerIp();