Add very rough client World support

This commit is contained in:
James Seibel
2023-02-25 15:29:40 -06:00
parent 03752509e7
commit 8d5926fb2d
5 changed files with 181 additions and 60 deletions
@@ -59,7 +59,6 @@ public class LodDataBuilder {
public static boolean canGenerateLodFromChunk(IChunkWrapper chunk)
{
//return true;
return chunk != null &&
chunk.isLightCorrect();
return chunk != null && chunk.isLightCorrect(); // TODO client only chunks return chunks with bad lighting, preventing chunk building (or transparent only chunks)
}
}
@@ -1,7 +1,13 @@
package com.seibel.lod.core.level;
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.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.lod.core.file.renderfile.RenderSourceFileHandler;
import com.seibel.lod.core.level.states.ClientRenderState;
import com.seibel.lod.core.pos.DhLodPos;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.render.LodQuadTree;
import com.seibel.lod.core.util.FileScanUtil;
import com.seibel.lod.core.file.fullDatafile.RemoteFullDataFileHandler;
@@ -12,6 +18,7 @@ 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;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.math.Mat4f;
import com.seibel.lod.core.render.renderer.LodRenderer;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
@@ -24,34 +31,32 @@ 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;
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 ClientOnlySaveStructure saveStructure;
public final RemoteFullDataFileHandler dataFileHandler;
public final RenderSourceFileHandler renderSourceFileHandler;
public final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer?
public final ChunkToLodBuilder chunkToLodBuilder = new ChunkToLodBuilder();;
public final IClientLevelWrapper level;
public LodRenderer renderer = null;
public LodQuadTree tree;
public final AtomicReference<ClientRenderState> ClientRenderStateRef = new AtomicReference<>();
public DhClientLevel(ClientOnlySaveStructure save, IClientLevelWrapper level)
public DhClientLevel(ClientOnlySaveStructure saveStructure, IClientLevelWrapper level)
{
this.save = save;
save.getDataFolder(level).mkdirs();
save.getRenderCacheFolder(level).mkdirs();
this.dataFileHandler = new RemoteFullDataFileHandler(this, save.getDataFolder(level));
this.renderSourceFileHandler = new RenderSourceFileHandler(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.renderSourceFileHandler);
this.renderBufferHandler = new RenderBufferHandler(this.tree);
this.saveStructure = saveStructure;
saveStructure.getDataFolder(level).mkdirs();
saveStructure.getRenderCacheFolder(level).mkdirs();
this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure.getDataFolder(level));
this.level = level;
FileScanUtil.scanFiles(save, level, this.dataFileHandler, this.renderSourceFileHandler);
LOGGER.info("Started DHLevel for {} with saves at {}", level, save);
FileScanUtil.scanFiles(saveStructure, level, this.dataFileHandler, null);
LOGGER.info("Started DHLevel for "+level+" with saves at "+ saveStructure);
}
@@ -65,18 +70,82 @@ public class DhClientLevel implements IDhClientLevel
@Override
public void clientTick()
{
this.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
this.renderBufferHandler.update();
ClientRenderState clientRenderState = this.ClientRenderStateRef.get();
if (clientRenderState == null)
{
return;
}
if (clientRenderState.quadtree.blockRenderDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH)
{
if (!this.ClientRenderStateRef.compareAndSet(clientRenderState, null))
{
return; //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;
}
}
clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
clientRenderState.renderer.bufferHandler.update();
this.chunkToLodBuilder.tick();
}
public void startRenderer(IClientLevelWrapper clientLevel)
{
LOGGER.info("Starting renderer for "+this);
ClientRenderState ClientRenderState = new ClientRenderState(this, clientLevel);
if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState))
{
LOGGER.warn("Failed to start renderer due to concurrency");
ClientRenderState.closeAsync();
}
}
@Override
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
{
if (this.renderer == null)
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState == null)
{
this.renderer = new LodRenderer(this.renderBufferHandler);
LOGGER.error("Tried to call render() on "+this+" when renderer has not been started!");
return;
}
this.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
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.
}
@Override
@@ -94,28 +163,70 @@ public class DhClientLevel implements IDhClientLevel
@Override
public void clearRenderDataCache()
{
if (this.tree != null)
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null && ClientRenderState.quadtree != null)
{
this.tree.clearRenderDataCache();
ClientRenderState.quadtree.clearRenderDataCache();
}
}
@Override
public void updateChunkAsync(IChunkWrapper chunk)
{
//TODO
CompletableFuture<ChunkSizedFullDataSource> 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.dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
}
}
@Override
public int getMinY() { return this.level.getMinHeight(); }
@Override
public CompletableFuture<Void> saveAsync() { return this.renderSourceFileHandler.flushAndSave(); }
public CompletableFuture<Void> saveAsync()
{
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null)
{
return ClientRenderState.renderSourceFileHandler.flushAndSave().thenCombine(this.dataFileHandler.flushAndSave(), (voidA, voidB) -> null);
}
else
{
return this.dataFileHandler.flushAndSave();
}
}
@Override
public void close()
{
this.renderSourceFileHandler.close();
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null)
{
while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null))
{
ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState == null)
{
return;
}
}
ClientRenderState.closeAsync().join(); //TODO: Make this async.
}
LOGGER.info("Closed DHLevel for "+this.level);
}
@@ -7,17 +7,14 @@ 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.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.lod.core.file.renderfile.RenderSourceFileHandler;
import com.seibel.lod.core.generation.BatchGenerator;
import com.seibel.lod.core.generation.WorldGenerationQueue;
import com.seibel.lod.core.level.states.ClientRenderState;
import com.seibel.lod.core.pos.DhLodPos;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.render.LodQuadTree;
import com.seibel.lod.core.file.fullDatafile.GeneratedFullDataFileHandler;
import com.seibel.lod.core.util.FileScanUtil;
import com.seibel.lod.core.pos.DhBlockPos2D;
import com.seibel.lod.core.render.RenderBufferHandler;
import com.seibel.lod.core.file.structure.LocalSaveStructure;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
@@ -25,7 +22,6 @@ import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.logging.f3.F3Screen;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.util.math.Mat4f;
import com.seibel.lod.core.render.renderer.LodRenderer;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -96,47 +92,33 @@ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel
@Override
public void clientTick()
{
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState == null)
ClientRenderState clientRenderState = this.ClientRenderStateRef.get();
if (clientRenderState == null)
{
return;
}
if (ClientRenderState.quadtree.blockRenderDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH)
if (clientRenderState.quadtree.blockRenderDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH)
{
if (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null))
if (!this.ClientRenderStateRef.compareAndSet(clientRenderState, null))
{
return; //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))
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();
clientRenderState.closeAsync();
return;
}
}
ClientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
ClientRenderState.renderer.bufferHandler.update();
}
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.dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
}
clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
clientRenderState.renderer.bufferHandler.update();
}
@Override
@@ -249,6 +231,19 @@ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel
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.dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
}
}
@Override
public void dumpRamUsage()
@@ -3,6 +3,7 @@ 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.renderfile.RenderSourceFileHandler;
import com.seibel.lod.core.level.DhClientLevel;
import com.seibel.lod.core.level.DhClientServerLevel;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.render.LodQuadTree;
@@ -27,7 +28,7 @@ public class ClientRenderState
public final LodRenderer renderer;
// TODO combine
public ClientRenderState(DhClientServerLevel parent, IClientLevelWrapper clientLevel)
{
this.clientLevel = clientLevel;
@@ -40,6 +41,18 @@ public class ClientRenderState
FileScanUtil.scanFiles(parent.saveStructure, parent.serverLevel, null, this.renderSourceFileHandler);
this.renderer = new LodRenderer(renderBufferHandler);
}
public ClientRenderState(DhClientLevel parent, IClientLevelWrapper clientLevel)
{
this.clientLevel = clientLevel;
this.renderSourceFileHandler = new RenderSourceFileHandler(parent.dataFileHandler, parent, parent.saveStructure.getRenderCacheFolder(parent.level));
this.quadtree = new LodQuadTree(parent, 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.level, null, this.renderSourceFileHandler);
this.renderer = new LodRenderer(renderBufferHandler);
}
@@ -52,7 +52,10 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
return null;
}
return new DhClientLevel(this.saveStructure, clientLevelWrapper);
DhClientLevel level = new DhClientLevel(this.saveStructure, clientLevelWrapper);
level.startRenderer(clientLevelWrapper);
return level;
});
}
@@ -93,7 +96,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
while (iterator.hasNext())
{
DhClientLevel level = iterator.next();
if (level.tree.blockRenderDistance != newBlockRenderDistance)
if (level.ClientRenderStateRef.get().quadtree.blockRenderDistance != newBlockRenderDistance)
{
level.close();
iterator.remove();