From 4a069b42d8eb0d99debe3c5a832d2bc3c8715e62 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 15 Jul 2023 08:48:33 -0500 Subject: [PATCH] Bandaid fix to holes in previously generated LOD sections --- .../fullDatafile/FullDataFileHandler.java | 65 ++++++++++++++----- 1 file changed, 47 insertions(+), 18 deletions(-) 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 b124c5cfd..be90f4fc5 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 @@ -43,13 +43,20 @@ public class FullDataFileHandler implements IFullDataSourceProvider private final ConcurrentHashMap unloadedFiles = new ConcurrentHashMap<>(); private final ConcurrentHashMap fileBySectionPos = new ConcurrentHashMap<>(); - public void ForEachFile(Consumer consumer) { - fileBySectionPos.values().forEach(consumer); - } + public void ForEachFile(Consumer consumer) { this.fileBySectionPos.values().forEach(consumer); } protected final IDhLevel level; protected final File saveDir; - protected final AtomicInteger topDetailLevel = new AtomicInteger(-1); + /** + * The starting value here denotes how far into the tree LOD writes should occur.
+ * This is a band-aid fix to prevent lower detail sections from not being generated until the detail level + * is requested.

+ * + * Note: that problem may still happen at a sufficiently large render distance, + * however this should kick the problem down the road + * far enough that it can be ignored for now. + */ + protected final AtomicInteger topDetailLevel = new AtomicInteger(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); protected final int minDetailLevel = CompleteFullDataSource.SECTION_SIZE_OFFSET; public FullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) @@ -84,7 +91,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider DhSectionPos pos = decodePositionByFile(file); if (pos != null) { unloadedFiles.put(pos, file); - this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel)); + this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); } } catch (Exception e) { @@ -167,7 +174,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider fileToUse = metaFiles.iterator().next(); } // Add file to the list of files. - this.topDetailLevel.updateAndGet(v -> Math.max(v, fileToUse.pos.sectionDetailLevel)); + this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, fileToUse.pos.sectionDetailLevel)); this.fileBySectionPos.put(pos, fileToUse); } } @@ -177,31 +184,46 @@ public class FullDataFileHandler implements IFullDataSourceProvider FullDataMetaFile metaFile = this.fileBySectionPos.get(pos); if (metaFile != null) return metaFile; - File fileToLoad = unloadedFiles.get(pos); + File fileToLoad = this.unloadedFiles.get(pos); // File does exist, but not loaded yet. - if (fileToLoad != null) { - synchronized (this) { + 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.fileBySectionPos.get(pos); - if (metaFile != null) return metaFile; // someone else loaded it already. - try { + if (metaFile != null) + { + return metaFile; // someone else loaded it already. + } + + try + { metaFile = new FullDataMetaFile(this, this.level, fileToLoad); - this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel)); + this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); this.fileBySectionPos.put(pos, metaFile); return metaFile; } - catch (IOException e) { + catch (IOException e) + { LOGGER.error("Failed to read data meta file at " + fileToLoad + ": ", e); FileUtil.renameCorruptedFile(fileToLoad); } - finally { - unloadedFiles.remove(pos); + finally + { + this.unloadedFiles.remove(pos); } } } - if (!allowCreateFile) return null; + + + if (!allowCreateFile) + { + return null; + } + // File does not exist, create it. // In this case, since 'creating' a file object doesn't actually do anything heavy on IO yet, we use CAS // to avoid overhead of 'synchronized', and eat the mini-overhead of possibly creating duplicate objects. @@ -214,8 +236,9 @@ public class FullDataFileHandler implements IFullDataSourceProvider LOGGER.error("IOException on creating new data file at {}", pos, e); return null; } + // This is a CAS with expected null value. - this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel)); + this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); FullDataMetaFile metaFileCas = this.fileBySectionPos.putIfAbsent(pos, metaFile); return metaFileCas == null ? metaFile : metaFileCas; } @@ -312,7 +335,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider @Override public CompletableFuture read(DhSectionPos pos) { - this.topDetailLevel.updateAndGet(intVal -> Math.max(intVal, pos.sectionDetailLevel)); + this.topDetailLevel.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); if (metaFile == null) { @@ -350,6 +373,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider private void writeChunkDataToMetaFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData) { FullDataMetaFile metaFile = this.fileBySectionPos.get(sectionPos); + if (metaFile == null) + { + // create a new file if one doesn't exist, + // this is done so we don't end up with holes where LODs should have been generated + metaFile = this.getLoadOrMakeFile(sectionPos, true); + } if (metaFile != null) { // there is a file for this position