Refactor Dh Client/Server/ClientServer Level objects
This commit is contained in:
@@ -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<Void> 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<Void> save() { return this.renderFileHandler.flushAndSave(); }
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
this.renderFileHandler.close();
|
||||
LOGGER.info("Closed DHLevel for {}", this.level);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Void> 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<Void> 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<Boolean> generatorEnabled;
|
||||
public F3Screen.NestedMessage f3Msg;
|
||||
public final AtomicReference<RenderState> renderState = new AtomicReference<>();
|
||||
public final AtomicReference<WorldGenState> 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<ChunkSizedData> 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<Void> 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<Boolean> generatorEnabled;
|
||||
public F3Screen.NestedMessage f3Msg;
|
||||
public final AtomicReference<RenderState> renderState = new AtomicReference<>();
|
||||
public final AtomicReference<WorldGenState> 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<ChunkSizedData> 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<Void> 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<Void> 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<Void> 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Void> 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<Void> 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user