From 3d62ca361db1a88bbe0aa59ff0c599c3e72536a2 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 2 Sep 2023 20:17:56 -0500 Subject: [PATCH] Refactor RenderSourceFileHandler and DataRenderTransformer --- .../methods/data/DhApiTerrainDataRepo.java | 2 +- .../fullData/FullDataDownSampler.java | 4 +- .../transformers/DataRenderTransformer.java | 10 +- .../fullDatafile/FullDataFileHandler.java | 4 +- .../GeneratedFullDataFileHandler.java | 5 +- .../fullDatafile/IFullDataSourceProvider.java | 5 +- .../renderfile/ILodRenderSourceProvider.java | 2 +- .../file/renderfile/RenderMetaDataFile.java | 59 +- .../renderfile/RenderSourceFileHandler.java | 547 ++++++++---------- .../SubDimensionLevelMatcher.java | 2 +- .../core/level/ClientLevelModule.java | 19 +- .../core/level/DhClientLevel.java | 5 +- .../core/level/DhClientServerLevel.java | 2 +- .../core/level/DhServerLevel.java | 2 +- .../core/util/FileScanUtil.java | 4 +- 15 files changed, 317 insertions(+), 355 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java index dadecd63d..63b2f074a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java @@ -212,7 +212,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo try { // attempt to get/generate the data source for this section - IFullDataSource dataSource = level.getFileHandler().read(sectionPos).get(); + IFullDataSource dataSource = level.getFileHandler().readAsync(sectionPos).get(); if (dataSource == null) { return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + sectionPos + "]."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataDownSampler.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataDownSampler.java index 33b3dfcfe..513f2594c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataDownSampler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataDownSampler.java @@ -54,7 +54,7 @@ public class FullDataDownSampler { for (int oz = 0; oz < sectionSizeNeeded; oz++) { - CompletableFuture future = provider.read(new DhSectionPos( + CompletableFuture future = provider.readAsync(new DhSectionPos( CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + ox, basePos.z + oz)); future = future.whenComplete((source, ex) -> { if (ex == null && source != null && source instanceof CompleteFullDataSource) @@ -78,7 +78,7 @@ public class FullDataDownSampler { for (int oz = 0; oz < CompleteFullDataSource.WIDTH; oz++) { - CompletableFuture future = provider.read(new DhSectionPos( + CompletableFuture future = provider.readAsync(new DhSectionPos( CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + ox * multiplier, basePos.z + oz * multiplier)); future = future.whenComplete((source, ex) -> { if (ex == null && source != null && source instanceof CompleteFullDataSource) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java index 92febfdd1..7506491e9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/DataRenderTransformer.java @@ -49,17 +49,17 @@ public class DataRenderTransformer // transformers // //==============// - public static CompletableFuture transformDataSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level) + public static CompletableFuture transformFullDataToRenderSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level) { - return CompletableFuture.supplyAsync(() -> transform(fullDataSource, level), transformerThreadPool); + return CompletableFuture.supplyAsync(() -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool); } - public static CompletableFuture transformDataSourceAsync(CompletableFuture fullDataSourceFuture, IDhClientLevel level) + public static CompletableFuture transformFullDataToRenderSourceAsync(CompletableFuture fullDataSourceFuture, IDhClientLevel level) { - return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transform(fullDataSource, level), transformerThreadPool); + return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transformFullDataToRenderSource(fullDataSource, level), transformerThreadPool); } - private static ColumnRenderSource transform(IFullDataSource fullDataSource, IDhClientLevel level) + private static ColumnRenderSource transformFullDataToRenderSource(IFullDataSource fullDataSource, IDhClientLevel level) { if (fullDataSource == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 6793f9e0e..cbd4a4c80 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -369,7 +369,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider * This call is concurrent. I.e. it supports being called by multiple threads at the same time. */ @Override - public CompletableFuture read(DhSectionPos pos) + public CompletableFuture readAsync(DhSectionPos pos) { this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); @@ -398,7 +398,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider /** This call is concurrent. I.e. it supports being called by multiple threads at the same time. */ @Override - public void write(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) + public void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { DhLodPos chunkPos = chunkDataView.getLodPos(); LodUtil.assertTrue(chunkPos.overlapsExactly(sectionPos.getSectionBBoxPos()), "Chunk " + chunkPos + " does not overlap section " + sectionPos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index f30617fce..b75608115 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -30,7 +30,6 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.DhLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import org.apache.logging.log4j.Logger; @@ -63,9 +62,9 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler //======// @Override - public CompletableFuture read(DhSectionPos pos) + public CompletableFuture readAsync(DhSectionPos pos) { - return super.read(pos); + return super.readAsync(pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java index dd06577b6..a295557dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/IFullDataSourceProvider.java @@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; -import com.seibel.distanthorizons.core.file.metaData.BaseMetaData; import com.seibel.distanthorizons.core.pos.DhSectionPos; import org.jetbrains.annotations.Nullable; @@ -36,8 +35,8 @@ public interface IFullDataSourceProvider extends AutoCloseable { void addScannedFile(Collection detectedFiles); - CompletableFuture read(DhSectionPos pos); - void write(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData); + CompletableFuture readAsync(DhSectionPos pos); + void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData); CompletableFuture flushAndSave(); CompletableFuture flushAndSave(DhSectionPos sectionPos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java index 9e44ac7e7..bbb3dc2dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/ILodRenderSourceProvider.java @@ -36,7 +36,7 @@ import java.util.concurrent.CompletableFuture; public interface ILodRenderSourceProvider extends AutoCloseable { CompletableFuture readAsync(DhSectionPos pos); - void addScannedFile(Collection detectedFiles); + void addScannedFiles(Collection detectedFiles); void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData); CompletableFuture flushAndSaveAsync(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java index 446e2afda..f92f64149 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.file.renderfile; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; import com.seibel.distanthorizons.core.file.metaData.BaseMetaData; @@ -33,7 +34,6 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.AtomicsUtil; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -228,11 +228,13 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements private CompletableFuture getCachedDataSourceAsync(boolean doTriggerUpdate) { // use the existing future - CompletableFuture renderSourceLoadFuture = renderSourceLoadFutureRef.get(); + CompletableFuture renderSourceLoadFuture = this.renderSourceLoadFutureRef.get(); if (renderSourceLoadFuture != null) { return renderSourceLoadFuture; } + + // attempt to get the cached render source ColumnRenderSource cachedRenderDataSource = this.cachedRenderDataSource.get(); if (cachedRenderDataSource == null) @@ -248,7 +250,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements CompletableFuture cas = AtomicsUtil.compareAndExchange(renderSourceLoadFutureRef, null, newFuture); if (cas == null) { - this.fileHandler.onReadRenderSourceLoadedFromCacheAsync(this, cachedRenderDataSource) + this.fileHandler.onRenderSourceLoadedFromCacheAsync(this, cachedRenderDataSource) // wait for the handler to finish before returning the renderSource .handle((voidObj, ex) -> { if (ex != null) @@ -280,30 +282,37 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements // load or create the render source if (!this.doesFileExist) { - // create a new Meta file - this.fileHandler.onCreateRenderFileAsync(this) - .thenApply((renderSource) -> - { - this.baseMetaData = this.makeMetaData(renderSource); - return renderSource; - }) - .thenCompose((renderSource) -> this.fileHandler.onRenderFileLoaded(renderSource, this)) + // create a new Meta file and render source + + + // create the new render source + byte dataDetailLevel = (byte) (this.pos.sectionDetailLevel - ColumnRenderSource.SECTION_SIZE_OFFSET); + int verticalSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(dataDetailLevel); + ColumnRenderSource newColumnRenderSource = new ColumnRenderSource(this.pos, verticalSize, level.getMinY()); + + this.baseMetaData = new BaseMetaData( + newColumnRenderSource.getSectionPos(), -1, newColumnRenderSource.getDataDetail(), + newColumnRenderSource.worldGenStep, RenderSourceFileHandler.RENDER_SOURCE_TYPE_ID, + newColumnRenderSource.getRenderDataFormatVersion(), Long.MAX_VALUE); + + this.fileHandler.onRenderFileLoadedAsync(newColumnRenderSource, this) .whenComplete((renderSource, ex) -> { if (ex != null) { if (!LodUtil.isInterruptOrReject(ex)) - LOGGER.error("Uncaught error on creation {}: ", this.file, ex); - cachedRenderDataSource = new SoftReference<>(null); - renderSourceLoadFutureRef.set(null); - future.complete(null); - } - else - { - cachedRenderDataSource = new SoftReference<>(renderSource); - renderSourceLoadFutureRef.set(null); - future.complete(renderSource); + { + LOGGER.error("Uncaught error on RenderMetaDataFile ColumnRenderSource creation for file: ["+this.file+"]. Error: ", ex); + } + + // set the render source to null to prevent instances where a corrupt or incomplete render source was returned + renderSource = null; } + + this.renderSourceLoadFutureRef.set(null); + + this.cachedRenderDataSource = new SoftReference<>(renderSource); + future.complete(renderSource); }); } else @@ -329,7 +338,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements return renderSource; }, fileReaderThreads) // TODO: Check for file version and only update if needed. - .thenCompose((renderSource) -> this.fileHandler.onRenderFileLoaded(renderSource, this)) + .thenCompose((renderSource) -> this.fileHandler.onRenderFileLoadedAsync(renderSource, this)) .whenComplete((renderSource, ex) -> { if (ex != null) @@ -351,12 +360,6 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements return future; } - private BaseMetaData makeMetaData(ColumnRenderSource renderSource) - { - return new BaseMetaData(renderSource.getSectionPos(), -1, - renderSource.getDataDetail(), renderSource.worldGenStep, RenderSourceFileHandler.RENDER_SOURCE_TYPE_ID, renderSource.getRenderDataFormatVersion(), Long.MAX_VALUE); - } - private FileInputStream getFileInputStream() throws IOException { FileInputStream fin = new FileInputStream(this.file); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 3e135be3c..6d8683ac0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.level.ClientLevelModule; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -39,8 +38,7 @@ import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.Reference; -import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; -import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -65,8 +63,9 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider private final ThreadPoolExecutor fileHandlerThreadPool; private final F3Screen.NestedMessage threadPoolMsg; - private final ConcurrentHashMap unloadedFiles = new ConcurrentHashMap<>(); - private final ConcurrentHashMap filesBySectionPos = new ConcurrentHashMap<>(); + private final ConcurrentHashMap unloadedFileBySectionPos = new ConcurrentHashMap<>(); + /** contains the loaded {@link RenderMetaDataFile}'s */ + private final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); private final IDhClientLevel level; private final File saveDir; @@ -74,12 +73,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider AtomicInteger topDetailLevel = new AtomicInteger(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); private final IFullDataSourceProvider fullDataSourceProvider; - enum TaskType - { - Read, UpdateReadData, Update, OnLoaded, - } - - private final WeakHashMap, TaskType> taskTracker = new WeakHashMap<>(); + private final WeakHashMap, ETaskType> taskTracker = new WeakHashMap<>(); @@ -104,57 +98,14 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider FileScanUtil.scanRenderFiles(saveStructure, level.getLevelWrapper(), this); } - /** Returns what should be displayed in Minecraft's F3 debug menu */ - private String[] f3Log() - { - ArrayList lines = new ArrayList<>(); - lines.add("Render Source File Handler [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); - lines.add(" Loaded files: " + this.filesBySectionPos.size() + " / " + (this.unloadedFiles.size() + this.filesBySectionPos.size())); - lines.add(" Thread pool tasks: " + fileHandlerThreadPool.getQueue().size() + " (completed: " + fileHandlerThreadPool.getCompletedTaskCount() + ")"); - - int totalFutures = taskTracker.size(); - EnumMap tasksOutstanding = new EnumMap<>(TaskType.class); - EnumMap tasksCompleted = new EnumMap<>(TaskType.class); - for (TaskType type : TaskType.values()) - { - tasksOutstanding.put(type, 0); - tasksCompleted.put(type, 0); - } - - synchronized (taskTracker) - { - for (Map.Entry, TaskType> entry : taskTracker.entrySet()) - { - if (entry.getKey().isDone()) - { - tasksCompleted.put(entry.getValue(), tasksCompleted.get(entry.getValue()) + 1); - } - else - { - tasksOutstanding.put(entry.getValue(), tasksOutstanding.get(entry.getValue()) + 1); - } - } - } - int totalOutstanding = tasksOutstanding.values().stream().mapToInt(Integer::intValue).sum(); - lines.add(" Futures: " + totalFutures + " (outstanding: " + totalOutstanding + ")"); - for (TaskType type : TaskType.values()) - { - lines.add(" " + type + ": " + tasksOutstanding.get(type) + " / " + (tasksOutstanding.get(type) + tasksCompleted.get(type))); - } - return lines.toArray(new String[0]); - } - - - //===============// - // file handling // - //===============// - /** * Caller must ensure that this method is called only once, - * and that the given files are not used before this method is called. + * and that the given files are not used before this method is called.

+ * + * Used by {@link FileScanUtil#scanRenderFiles(AbstractSaveStructure, ILevelWrapper, ILodRenderSourceProvider)} */ @Override - public void addScannedFile(Collection detectedFiles) + public void addScannedFiles(Collection detectedFiles) { if (USE_LAZY_LOADING) { @@ -165,7 +116,6 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider this.immediateAddScannedFile(detectedFiles); } } - private void lazyAddScannedFile(Collection detectedFiles) { for (File file : detectedFiles) @@ -180,11 +130,11 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider try { - DhSectionPos pos = this.decodePositionByFile(file); + DhSectionPos pos = this.decodePositionFromFileName(file); if (pos != null) { - this.unloadedFiles.put(pos, file); - this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel)); + this.unloadedFileBySectionPos.put(pos, file); + this.topDetailLevel.updateAndGet(currentTopDetailLevel -> Math.max(currentTopDetailLevel, pos.sectionDetailLevel)); } } catch (Exception e) @@ -194,7 +144,6 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } } } - private void immediateAddScannedFile(Collection newRenderFiles) { HashMultimap filesByPos = HashMultimap.create(); @@ -272,86 +221,19 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider fileToUse = metaFiles.iterator().next(); } // Add this file to the list of files. - this.filesBySectionPos.put(pos, fileToUse); + this.metaFileBySectionPos.put(pos, fileToUse); // increase the lowest detail level if a new lower detail file is found this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel)); } } - protected RenderMetaDataFile getLoadOrMakeFile(DhSectionPos pos, boolean allowCreateFile) - { - RenderMetaDataFile metaFile = this.filesBySectionPos.get(pos); - if (metaFile != null) - { - return metaFile; - } - - - File fileToLoad = this.unloadedFiles.get(pos); - if (fileToLoad != null && !fileToLoad.exists()) - { - fileToLoad = null; - this.unloadedFiles.remove(pos); - } - - // File does exist, but not loaded yet. - if (fileToLoad != null) - { - synchronized (this) - { - // Double check locking for loading file, as loading file means also loading the metadata, which - // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the - // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. - metaFile = this.filesBySectionPos.get(pos); - if (metaFile != null) - { - return metaFile; // someone else loaded it already. - } - - try - { - metaFile = RenderMetaDataFile.createFromExistingFile(this, fileToLoad); - this.topDetailLevel.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel)); - this.filesBySectionPos.put(pos, metaFile); - return metaFile; - } - catch (IOException e) - { - LOGGER.error("Failed to read render meta file at " + fileToLoad + ": ", e); - FileUtil.renameCorruptedFile(fileToLoad); - } - finally - { - this.unloadedFiles.remove(pos); - } - } - } - - - if (!allowCreateFile) - { - return null; - } - - // File probably doesn't exist, try creating it. - try - { - // createFromExistingOrNewFile is due to a rare issue where the file may already exist but isn't in the file list - metaFile = RenderMetaDataFile.createFromExistingOrNewFile(this, pos); - } - catch (IOException e) - { - LOGGER.error("IOException on creating new data file at {}", pos, e); - return null; - } - - this.topDetailLevel.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel)); - // This is a CAS with expected null value. - RenderMetaDataFile metaFileCas = this.filesBySectionPos.putIfAbsent(pos, metaFile); - return metaFileCas == null ? metaFile : metaFileCas; - } - /** This call is concurrent. I.e. it supports multiple threads calling this method at the same time. */ + + //===============// + // file handling // + //===============// + + /** This call is thread safe and can be called concurrently from multiple threads. */ @Override public CompletableFuture readAsync(DhSectionPos pos) { @@ -361,20 +243,20 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider return CompletableFuture.completedFuture(null); } - RenderMetaDataFile metaFile = this.getLoadOrMakeFile(pos, true); - // On error, (when it returns null,) return an empty render source + + RenderMetaDataFile metaFile = this.getLoadOrMakeFile(pos); if (metaFile == null) { return CompletableFuture.completedFuture(ColumnRenderSource.createEmptyRenderSource(pos)); } - CompletableFuture future = metaFile.loadOrGetCachedDataSourceAsync(this.fileHandlerThreadPool, this.level).handle( - (renderSource, exception) -> + CompletableFuture getDataSourceFuture = metaFile.loadOrGetCachedDataSourceAsync(this.fileHandlerThreadPool, this.level) + .handle((renderSource, exception) -> { if (exception != null) { - LOGGER.error("Uncaught error on " + pos + ":", exception); + LOGGER.error("Uncaught error in readAsync for pos: " + pos + ". Error:", exception); } return (renderSource != null) ? renderSource : ColumnRenderSource.createEmptyRenderSource(pos); @@ -382,18 +264,89 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider synchronized (this.taskTracker) { - this.taskTracker.put(future, TaskType.Read); + this.taskTracker.put(getDataSourceFuture, ETaskType.READ); } - return future; + return getDataSourceFuture; } - - public CompletableFuture onCreateRenderFileAsync(RenderMetaDataFile file) + /** @return null if there was an issue */ + private RenderMetaDataFile getLoadOrMakeFile(DhSectionPos pos) { - final int verticalSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get() - .calculateMaxVerticalData((byte) (file.pos.sectionDetailLevel - ColumnRenderSource.SECTION_SIZE_OFFSET)); + RenderMetaDataFile metaFile = this.metaFileBySectionPos.get(pos); + if (metaFile != null) + { + // return the loaded file + return metaFile; + } - return CompletableFuture.completedFuture( - new ColumnRenderSource(file.pos, verticalSize, this.level.getMinY())); + + // we don't have a loaded file, for that pos, + // do we have an unloaded file for that pos? + File fileToLoad = this.unloadedFileBySectionPos.get(pos); + if (fileToLoad != null && !fileToLoad.exists()) + { + fileToLoad = null; + this.unloadedFileBySectionPos.remove(pos); + } + + + if (fileToLoad != null) + { + // A file exists, but isn't loaded yet. + + // Double check locking for loading file, as loading file means also loading the metadata, which + // while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the + // duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it. + synchronized (this) + { + // check if another thread already finished loading this file + metaFile = this.metaFileBySectionPos.get(pos); + if (metaFile != null) + { + return metaFile; + } + + + // attempt to load the file + try + { + metaFile = RenderMetaDataFile.createFromExistingFile(this, fileToLoad); + this.topDetailLevel.updateAndGet(currentTopDetailLevel -> Math.max(currentTopDetailLevel, pos.sectionDetailLevel)); + this.metaFileBySectionPos.put(pos, metaFile); + return metaFile; + } + catch (IOException e) + { + LOGGER.error("Failed to read render meta file at " + fileToLoad + ": ", e); + FileUtil.renameCorruptedFile(fileToLoad); + } + finally + { + this.unloadedFileBySectionPos.remove(pos); + } + } + } + + + // Either no file exists for this position + // or the existing file was corrupted. + // Create a new file. + try + { + // createFromExistingOrNewFile() is used instead of createFromExistingFile() + // due to a rare issue where the file may already exist but isn't in the file list + metaFile = RenderMetaDataFile.createFromExistingOrNewFile(this, pos); + + this.topDetailLevel.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel)); + + // Compare And Swap to handle a concurrency issue where multiple threads created the same Meta File at the same time + RenderMetaDataFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); + return (metaFileCas == null) ? metaFile : metaFileCas; + } + catch (IOException e) + { + LOGGER.error("IOException on creating new data file at "+pos, e); + return null; + } } @@ -403,107 +356,133 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider //=============// /** - * This call is concurrent. I.e. it supports multiple threads calling this method at the same time.
+ * This call is thread safe and can be called concurrently from multiple threads.
* This allows fast writes of new data to the render source, without having to wait for the data to be written to disk. */ @Override public void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { // convert to the lowest detail level so all detail levels are updated - this.fastWriteDataToSourceRecursively(chunkDataView, DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); - this.fullDataSourceProvider.write(sectionPos, chunkDataView); + this.writeChunkDataToFileRecursively(chunkDataView, DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + this.fullDataSourceProvider.writeChunkDataToFile(sectionPos, chunkDataView); } - - private void fastWriteDataToSourceRecursively(ChunkSizedFullDataAccessor chunk, byte sectionDetailLevel) + private void writeChunkDataToFileRecursively(ChunkSizedFullDataAccessor chunk, byte sectionDetailLevel) { DhLodPos boundingPos = chunk.getLodPos(); - DhLodPos sectPosMin = boundingPos.convertToDetailLevel(sectionDetailLevel); - int width = sectionDetailLevel > boundingPos.detailLevel ? 1 : boundingPos.getWidthAtDetail(sectionDetailLevel); - for (int ox = 0; ox < width; ox++) + DhLodPos minSectionPos = boundingPos.convertToDetailLevel(sectionDetailLevel); + + int width = (sectionDetailLevel > boundingPos.detailLevel) ? 1 : boundingPos.getWidthAtDetail(sectionDetailLevel); + for (int xOffset = 0; xOffset < width; xOffset++) { - for (int oz = 0; oz < width; oz++) + for (int zOffset = 0; zOffset < width; zOffset++) { - DhSectionPos sectPos = new DhSectionPos(sectionDetailLevel, sectPosMin.x + ox, sectPosMin.z + oz); - RenderMetaDataFile metaFile = this.filesBySectionPos.get(sectPos); // bypass the getLoadOrMakeFile(), as we only want in-cache files. + DhSectionPos sectionPos = new DhSectionPos(sectionDetailLevel, minSectionPos.x + xOffset, minSectionPos.z + zOffset); + RenderMetaDataFile metaFile = this.metaFileBySectionPos.get(sectionPos); // bypass the getLoadOrMakeFile() since we only want cached files. if (metaFile != null) { metaFile.updateChunkIfSourceExists(chunk, this.level); } } } - if (sectionDetailLevel < topDetailLevel.get()) + + if (sectionDetailLevel < this.topDetailLevel.get()) { - fastWriteDataToSourceRecursively(chunk, (byte) (sectionDetailLevel + 1)); + this.writeChunkDataToFileRecursively(chunk, (byte) (sectionDetailLevel + 1)); } } - /** This call is concurrent. I.e. it supports multiple threads calling this method at the same time. */ + /** This call is thread safe and can be called concurrently from multiple threads. */ @Override public CompletableFuture flushAndSaveAsync() { LOGGER.info("Shutting down " + RenderSourceFileHandler.class.getSimpleName() + "..."); ArrayList> futures = new ArrayList<>(); - for (RenderMetaDataFile metaFile : this.filesBySectionPos.values()) + for (RenderMetaDataFile metaFile : this.metaFileBySectionPos.values()) { futures.add(metaFile.flushAndSaveAsync(this.fileHandlerThreadPool)); } return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .whenComplete((voidObj, exception) -> LOGGER.info("Finished shutting down " + RenderSourceFileHandler.class.getSimpleName())); + .whenComplete((voidObj, exception) -> LOGGER.info("Finished saving " + RenderSourceFileHandler.class.getSimpleName())); } - //================// - // cache updating // - //================// + //==========================// + // meta file cache updating // + //==========================// - private CompletableFuture updateCacheAsync(ColumnRenderSource renderSource, RenderMetaDataFile file) + public CompletableFuture onRenderFileLoadedAsync(ColumnRenderSource renderSource, RenderMetaDataFile file) { - DebugRenderer.BoxWithLife box = new DebugRenderer.BoxWithLife(new DebugRenderer.Box(renderSource.sectionPos, 74f, 86f, 0.1f, Color.red), 1.0, 32f, Color.green.darker()); + CompletableFuture future = this.updateMetaFileCacheAsync(renderSource, file).handle((voidObj, ex) -> renderSource); + + synchronized (this.taskTracker) + { + this.taskTracker.put(future, ETaskType.ON_LOADED); + } + return future; + } + + public CompletableFuture onRenderSourceLoadedFromCacheAsync(RenderMetaDataFile file, ColumnRenderSource renderSource) { return this.updateMetaFileCacheAsync(renderSource, file); } + + private CompletableFuture updateMetaFileCacheAsync(ColumnRenderSource renderSource, RenderMetaDataFile renderMetaFile) + { + DebugRenderer.BoxWithLife debugBox = new DebugRenderer.BoxWithLife(new DebugRenderer.Box(renderSource.sectionPos, 74f, 86f, 0.1f, Color.red), 1.0, 32f, Color.green.darker()); + // Skip updating the cache if the data file is already up-to-date - FullDataMetaFile dataFile = this.fullDataSourceProvider.getFileIfExist(file.pos); - if (!ALWAYS_INVALIDATE_CACHE && dataFile != null && dataFile.baseMetaData != null && dataFile.baseMetaData.checksum == file.baseMetaData.dataVersion.get()) { - LOGGER.debug("Skipping render cache update for {}", file.pos); + FullDataMetaFile dataFile = this.fullDataSourceProvider.getFileIfExist(renderMetaFile.pos); + if (!ALWAYS_INVALIDATE_CACHE && dataFile != null && dataFile.baseMetaData != null && dataFile.baseMetaData.checksum == renderMetaFile.baseMetaData.dataVersion.get()) // TODO can we make it so the version comparisons either both use the checksum or the dataVersion? Comparing checksum and dataVersion is kinda confusing + { + LOGGER.debug("Skipping render cache update for " + renderMetaFile.pos); renderSource.localVersion.incrementAndGet(); return CompletableFuture.completedFuture(null); } - // get the full data source loading future - final Reference targetChecksumVersion = new Reference<>(Long.MAX_VALUE); - CompletableFuture fullDataSourceFuture = - this.fullDataSourceProvider.read(renderSource.getSectionPos()) - .thenApply((fullDataSource) -> { - // the fullDataSource can be null if the thread this was running on was interrupted - box.box.color = Color.yellow.darker(); - FullDataMetaFile file2 = this.fullDataSourceProvider.getFileIfExist(file.pos); - targetChecksumVersion.value = file2 == null ? Long.MAX_VALUE : file2.baseMetaData.checksum; - return fullDataSource; - }).exceptionally((ex) -> - { - LOGGER.error("Exception when getting data for updateCache()", ex); - return null; - }); - synchronized (taskTracker) + + final Reference renderDataVersionRef = new Reference<>(Integer.MAX_VALUE); + + // get the full data source + CompletableFuture fullDataSourceFuture = + this.fullDataSourceProvider.readAsync(renderSource.getSectionPos()) + .thenApply((fullDataSource) -> + { + debugBox.box.color = Color.yellow.darker(); + + // get the metaFile's version + FullDataMetaFile renderSourceMetaFile = this.fullDataSourceProvider.getFileIfExist(renderMetaFile.pos); + if (renderSourceMetaFile != null) + { + renderDataVersionRef.value = renderSourceMetaFile.baseMetaData.checksum; + } + + return fullDataSource; + }).exceptionally((ex) -> + { + LOGGER.error("Exception when getting data for updateCache()", ex); + return null; + }); + + synchronized (this.taskTracker) { - taskTracker.put(fullDataSourceFuture, TaskType.UpdateReadData); + this.taskTracker.put(fullDataSourceFuture, ETaskType.UPDATE_READ_DATA); } + + // convert the full data source into a render source - //LOGGER.info("Recreating cache for {}", data.getSectionPos()); - CompletableFuture transformFuture = DataRenderTransformer.transformDataSourceAsync(fullDataSourceFuture, this.level) + CompletableFuture transformFuture = DataRenderTransformer.transformFullDataToRenderSourceAsync(fullDataSourceFuture, this.level) .handle((newRenderSource, ex) -> { if (ex == null) { try { - file.baseMetaData.dataVersion.set(targetChecksumVersion.value); - this.writeRenderSourceToFile(renderSource, file, newRenderSource); + renderMetaFile.baseMetaData.dataVersion.set(renderDataVersionRef.value); + this.mergeRenderSourcesAndWriteToFile(renderSource, renderMetaFile, newRenderSource); } catch (Throwable e) { @@ -514,42 +493,24 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { LOGGER.error("Exception when updating render file using data source: ", ex); } - else - { - //LOGGER.info("Interrupted update of render file using data source: ", ex); - } - box.close(); + + debugBox.close(); return null; }); - synchronized (taskTracker) + + synchronized (this.taskTracker) { - taskTracker.put(transformFuture, TaskType.Update); + this.taskTracker.put(transformFuture, ETaskType.UPDATE); } + + return transformFuture; } - public CompletableFuture onRenderFileLoaded(ColumnRenderSource renderSource, RenderMetaDataFile file) - { - CompletableFuture future = this.updateCacheAsync(renderSource, file).handle((voidObj, ex) -> { - if (ex != null && !LodUtil.isInterruptOrReject(ex)) - { - LOGGER.error("Exception when updating render file using data source: ", ex); - } - return renderSource; - }); - synchronized (taskTracker) - { - taskTracker.put(future, TaskType.OnLoaded); - } - return future; - } - public CompletableFuture onReadRenderSourceLoadedFromCacheAsync(RenderMetaDataFile file, ColumnRenderSource data) - { - return this.updateCacheAsync(data, file); - } - private void writeRenderSourceToFile(ColumnRenderSource currentRenderSource, RenderMetaDataFile file, ColumnRenderSource newRenderSource) + + private void mergeRenderSourcesAndWriteToFile(ColumnRenderSource currentRenderSource, RenderMetaDataFile metaFile, ColumnRenderSource newRenderSource) { if (currentRenderSource == null || newRenderSource == null) { @@ -558,85 +519,72 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider currentRenderSource.updateFromRenderSource(newRenderSource); - //file.metaData.dataVersion.set(newDataVersion); - file.baseMetaData.dataLevel = currentRenderSource.getDataDetail(); - file.baseMetaData.dataTypeId = RENDER_SOURCE_TYPE_ID; - file.baseMetaData.binaryDataFormatVersion = currentRenderSource.getRenderDataFormatVersion(); - file.save(currentRenderSource); + metaFile.baseMetaData.dataLevel = currentRenderSource.getDataDetail(); + metaFile.baseMetaData.dataTypeId = RENDER_SOURCE_TYPE_ID; + metaFile.baseMetaData.binaryDataFormatVersion = currentRenderSource.getRenderDataFormatVersion(); + metaFile.save(currentRenderSource); } -/* - public boolean refreshRenderSource(ColumnRenderSource renderSource) - { - RenderMetaDataFile file = this.filesBySectionPos.get(renderSource.getSectionPos()); - if (renderSource.isEmpty()) - { - if (file == null || file.baseMetaData == null) - { - return false; - } - } - - LodUtil.assertTrue(file != null); - LodUtil.assertTrue(file.baseMetaData != null); -// if (!this.fullDataSourceProvider.isCacheVersionValid(file.pos, file.metaData.dataVersion.get())) -// { - this.updateCacheAsync(renderSource, file).join(); - return true; -// } - -// return false; - } - */ + + + //=========// + // F3 menu // + //=========// + + /** Returns what should be displayed in Minecraft's F3 debug menu */ + private String[] f3Log() + { + ArrayList lines = new ArrayList<>(); + lines.add("Render Source File Handler [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); + lines.add(" Loaded files: " + this.metaFileBySectionPos.size() + " / " + (this.unloadedFileBySectionPos.size() + this.metaFileBySectionPos.size())); + lines.add(" Thread pool tasks: " + this.fileHandlerThreadPool.getQueue().size() + " (completed: " + this.fileHandlerThreadPool.getCompletedTaskCount() + ")"); + + int totalFutures = this.taskTracker.size(); + EnumMap tasksOutstanding = new EnumMap<>(ETaskType.class); + EnumMap tasksCompleted = new EnumMap<>(ETaskType.class); + for (ETaskType type : ETaskType.values()) + { + tasksOutstanding.put(type, 0); + tasksCompleted.put(type, 0); + } + + synchronized (this.taskTracker) + { + for (Map.Entry, ETaskType> entry : this.taskTracker.entrySet()) + { + if (entry.getKey().isDone()) + { + tasksCompleted.put(entry.getValue(), tasksCompleted.get(entry.getValue()) + 1); + } + else + { + tasksOutstanding.put(entry.getValue(), tasksOutstanding.get(entry.getValue()) + 1); + } + } + } + int totalOutstanding = tasksOutstanding.values().stream().mapToInt(Integer::intValue).sum(); + lines.add(" Futures: " + totalFutures + " (outstanding: " + totalOutstanding + ")"); + for (ETaskType type : ETaskType.values()) + { + lines.add(" " + type + ": " + tasksOutstanding.get(type) + " / " + (tasksOutstanding.get(type) + tasksCompleted.get(type))); + } + return lines.toArray(new String[0]); + } + + //=====================// // clearing / shutdown // //=====================// - //private static CompletableFuture cleanupTask; - @Override public void close() { - LOGGER.info("Closing " + this.getClass().getSimpleName() + " with [" + this.filesBySectionPos.size() + "] files..."); - /* - // queue the file save futures - ArrayList> futures = new ArrayList<>(); - for (RenderMetaDataFile metaFile : this.filesBySectionPos.values()) - { - CompletableFuture saveFuture = metaFile.flushAndSaveAsync(fileHandlerThreadPool); - if (!saveFuture.isDone()) - { - futures.add(saveFuture); - } - } - - - if (futures.size() != 0) - { - LOGGER.info("Waiting for ["+futures.size()+"] files to save..."); - - // if the save futures didn't already complete, wait for them and then shut down the thread pool - CompletableFuture combinedFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); - cleanupTask = combinedFuture.handle((result, ex) -> { - if (ex != null && !LodUtil.isInterruptOrReject(ex)) { - LOGGER.error("Exception when waiting for render source files to save", ex); - } - return null; - }).thenRun(() -> - { - LOGGER.info("Finished closing "+this.getClass().getSimpleName()+", ["+futures.size()+"] files were saved out of ["+this.filesBySectionPos.size()+"] total files."); - fileHandlerThreadPool.shutdown(); - threadPoolMsg.close(); - }); - } - else {*/ - fileHandlerThreadPool.shutdown(); - threadPoolMsg.close(); - //} + LOGGER.info("Closing " + this.getClass().getSimpleName() + " with [" + this.metaFileBySectionPos.size() + "] files..."); + this.fileHandlerThreadPool.shutdown(); + this.threadPoolMsg.close(); } - public void deleteRenderCache() { // delete each file in the cache directory @@ -653,7 +601,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } // clear the cached files - this.filesBySectionPos.clear(); + this.metaFileBySectionPos.clear(); } @@ -665,7 +613,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider public File computeRenderFilePath(DhSectionPos pos) { return new File(this.saveDir, pos.serialize() + RENDER_FILE_POSTFIX); } @Nullable - public DhSectionPos decodePositionByFile(File file) + public DhSectionPos decodePositionFromFileName(File file) { String fileName = file.getName(); if (!fileName.endsWith(RENDER_FILE_POSTFIX)) return null; @@ -673,4 +621,21 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider return DhSectionPos.deserialize(fileName); } + + + //================// + // helper classes // + //================// + + /** + * READ
+ * UPDATE_READ_DATA
+ * UPDATE
+ * ON_LOADED
+ */ + private enum ETaskType + { + READ, UPDATE_READ_DATA, UPDATE, ON_LOADED, + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index 767a2ebf5..710f0e1b4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -242,7 +242,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable } IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), clientLevelWrapper); IFullDataSourceProvider fileHandler = new FullDataFileHandler(tempLevel, tempLevel.getSaveStructure()); - CompletableFuture testDataSource = fileHandler.read(new DhSectionPos(playerChunkPos)); + CompletableFuture testDataSource = fileHandler.readAsync(new DhSectionPos(playerChunkPos)); IFullDataSource lodDataSource = testDataSource.get(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index 5337107ce..ac3ec177c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -50,12 +50,12 @@ public class ClientLevelModule implements Closeable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private final IDhClientLevel parent; + private final IDhClientLevel parentClientLevel; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); public final F3Screen.NestedMessage f3Message; - public ClientLevelModule(IDhClientLevel parent) + public ClientLevelModule(IDhClientLevel parentClientLevel) { - this.parent = parent; + this.parentClientLevel = parentClientLevel; this.f3Message = new F3Screen.NestedMessage(this::f3Log); } @@ -88,7 +88,7 @@ public class ClientLevelModule implements Closeable } clientRenderState.close(); - clientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure()); + clientRenderState = new ClientRenderState(parentClientLevel, parentClientLevel.getFileHandler(), parentClientLevel.getSaveStructure()); if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState)) { //FIXME: How to handle this? @@ -120,7 +120,7 @@ public class ClientLevelModule implements Closeable /** @return if the {@link ClientRenderState} was successfully swapped */ public boolean startRenderer() { - ClientRenderState ClientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure()); + ClientRenderState ClientRenderState = new ClientRenderState(parentClientLevel, parentClientLevel.getFileHandler(), parentClientLevel.getSaveStructure()); if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState)) { LOGGER.warn("Failed to start renderer due to concurrency"); @@ -173,17 +173,18 @@ public class ClientLevelModule implements Closeable //===============// // data handling // //===============// - public void saveWrites(ChunkSizedFullDataAccessor data) + public void writeChunkDataToFile(ChunkSizedFullDataAccessor data) { - ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); + + ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState != null) { ClientRenderState.renderSourceFileHandler.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); } else { - parent.getFileHandler().write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + this.parentClientLevel.getFileHandler().writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); } } @@ -240,7 +241,7 @@ public class ClientLevelModule implements Closeable /** Returns what should be displayed in Minecraft's F3 debug menu */ protected String[] f3Log() { - String dimName = parent.getClientLevelWrapper().getDimensionType().getDimensionName(); + String dimName = parentClientLevel.getClientLevelWrapper().getDimensionType().getDimensionName(); ClientRenderState renderState = this.ClientRenderStateRef.get(); if (renderState == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index ac38b49d9..b48735765 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -102,10 +102,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel } @Override - public void saveWrites(ChunkSizedFullDataAccessor data) - { - clientside.saveWrites(data); - } + public void saveWrites(ChunkSizedFullDataAccessor data) { this.clientside.writeChunkDataToFile(data); } @Override public int getMinY() { return levelWrapper.getMinHeight(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 355f76373..3156f0666 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -176,7 +176,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS @Override public void saveWrites(ChunkSizedFullDataAccessor data) { - clientside.saveWrites(data); + clientside.writeChunkDataToFile(data); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 84ab0fb4a..2bee0ac1c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -59,7 +59,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void saveWrites(ChunkSizedFullDataAccessor data) { DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); - getFileHandler().write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + getFileHandler().writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/FileScanUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/FileScanUtil.java index 430897d5f..8db0eb414 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/FileScanUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/FileScanUtil.java @@ -21,13 +21,11 @@ package com.seibel.distanthorizons.core.util; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.renderfile.ILodRenderSourceProvider; -import com.seibel.distanthorizons.core.file.renderfile.RenderSourceFileHandler; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.Logger; -import javax.annotation.Nullable; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; @@ -69,7 +67,7 @@ public class FileScanUtil path -> path.toFile().getName().endsWith(RENDER_FILE_POSTFIX) && path.toFile().isFile() ).map(Path::toFile).collect(Collectors.toList()); LOGGER.info("Found " + files.size() + " render cache files for " + levelWrapper + " in " + saveStructure); - renderSourceProvider.addScannedFile(files); + renderSourceProvider.addScannedFiles(files); } catch (Exception e) {