diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java index 9eb09fad1..eb4c0ac08 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java @@ -98,6 +98,7 @@ public class ColumnRenderSource this.verticalDataCount = parsedColumnData.verticalSize; this.renderDataContainer = parsedColumnData.dataContainer; this.worldGenStep = parsedColumnData.worldGenStep; + this.isEmpty = parsedColumnData.isEmpty; this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE]; this.fillDebugFlag(0, 0, SECTION_SIZE, SECTION_SIZE, DebugSourceFlag.FILE); @@ -263,6 +264,7 @@ public class ColumnRenderSource this.debugSourceFlags[i / this.verticalDataCount] = renderSource.debugSourceFlags[i / this.verticalDataCount]; } } + localVersion.incrementAndGet(); } /** * If the newVerticalSize is different than the current verticalSize, @@ -275,6 +277,7 @@ public class ColumnRenderSource { this.verticalDataCount = newVerticalSize; this.renderDataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount]; + localVersion.incrementAndGet(); } } 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 f6eaf83b6..ee8d0c2f7 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 @@ -185,9 +185,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider } } - - - protected FullDataMetaFile getLoadOrMakeFile(DhSectionPos pos, boolean allowCreateFile) { FullDataMetaFile metaFile = this.fileBySectionPos.get(pos); @@ -603,6 +600,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider @Override public ExecutorService getIOExecutor() { return fileHandlerThreadPool; } + @Override + public FullDataMetaFile getFileIfExist(DhSectionPos pos) + { + return getLoadOrMakeFile(pos, false); + } + //=========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index a229b309c..0480386f3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -358,7 +358,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { AbstractFullDataSourceLoader loader = AbstractFullDataSourceLoader.getLoader(data.getClass(), data.getBinaryDataFormatVersion()); return new BaseMetaData(data.getSectionPos(), -1, - data.getDataDetailLevel(), data.getWorldGenStep(), (loader == null ? 0 : loader.datatypeId), data.getBinaryDataFormatVersion()); + data.getDataDetailLevel(), data.getWorldGenStep(), (loader == null ? 0 : loader.datatypeId), data.getBinaryDataFormatVersion(), Long.MAX_VALUE); } /** 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 c3de08a84..6f993cb96 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 @@ -4,6 +4,7 @@ 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.metaData.BaseMetaData; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Collection; @@ -30,5 +31,7 @@ public interface IFullDataSourceProvider extends AutoCloseable CompletableFuture onDataFileUpdate(IFullDataSource source, FullDataMetaFile file, Consumer onUpdated, Function updater); File computeDataFilePath(DhSectionPos pos); ExecutorService getIOExecutor(); - + + @Nullable + FullDataMetaFile getFileIfExist(DhSectionPos pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java index c19cb3116..1248de29d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/AbstractMetaDataContainerFile.java @@ -155,11 +155,11 @@ public abstract class AbstractMetaDataContainerFile byte loaderVersion = byteBuffer.get(); EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(byteBuffer.get()); long dataTypeId = byteBuffer.getLong(); - long unusedTimestamp = byteBuffer.getLong(); // not currently implemented + long dataVersion = byteBuffer.getLong(); // data versioning LodUtil.assertTrue(byteBuffer.remaining() == METADATA_RESERVED_SIZE); DhSectionPos dataPos = new DhSectionPos(detailLevel, x, z); - return new BaseMetaData(dataPos, checksum, dataLevel, worldGenStep, dataTypeId, loaderVersion); + return new BaseMetaData(dataPos, checksum, dataLevel, worldGenStep, dataTypeId, loaderVersion, dataVersion); } } @@ -214,13 +214,12 @@ public abstract class AbstractMetaDataContainerFile try (FileChannel fileChannel = FileChannel.open(tempFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { fileChannel.position(METADATA_SIZE_IN_BYTES); - int checksum; try (DhDataOutputStream compressedOut = new DhDataOutputStream(Channels.newOutputStream(fileChannel)); CheckedOutputStream checkedOut = new CheckedOutputStream(compressedOut, new Adler32())) // TODO: Is Adler32 ok? { dataWriterFunc.writeBufferToFile(compressedOut); - checksum = (int) checkedOut.getChecksum().getValue(); + this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue(); } @@ -231,13 +230,13 @@ public abstract class AbstractMetaDataContainerFile buffer.putInt(this.pos.sectionX); buffer.putInt(Integer.MIN_VALUE); // Unused - y pos buffer.putInt(this.pos.sectionZ); - buffer.putInt(checksum); + buffer.putInt(this.baseMetaData.checksum); buffer.put(this.pos.sectionDetailLevel); buffer.put(this.baseMetaData.dataLevel); buffer.put(this.baseMetaData.binaryDataFormatVersion); buffer.put(this.baseMetaData.worldGenStep != null ? this.baseMetaData.worldGenStep.value : EDhApiWorldGenerationStep.EMPTY.value); // TODO this null check shouldn't be necessary buffer.putLong(this.baseMetaData.dataTypeId); - buffer.putLong(Long.MAX_VALUE); //buff.putLong(this.metaData.dataVersion.get()); // not currently implemented + buffer.putLong(this.baseMetaData.dataVersion.get()); // for types that doesn't need data versioning, this will be Long.MAX_VALUE LodUtil.assertTrue(buffer.remaining() == METADATA_RESERVED_SIZE); buffer.flip(); fileChannel.write(buffer); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/BaseMetaData.java b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/BaseMetaData.java index fefbcc5ef..be595b5d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/BaseMetaData.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/metaData/BaseMetaData.java @@ -5,6 +5,8 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.I import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; +import java.util.concurrent.atomic.AtomicLong; + /** * Contains and represents the meta information ({@link DhSectionPos}, {@link BaseMetaData#dataLevel}, etc.) * stored at the beginning of files that use the {@link AbstractMetaDataContainerFile}.
@@ -14,7 +16,7 @@ public class BaseMetaData { public DhSectionPos pos; public int checksum; - // public AtomicLong dataVersion; // currently broken + public AtomicLong dataVersion = new AtomicLong(Long.MAX_VALUE); public byte dataLevel; // TODO what does this represent? public EDhApiWorldGenerationStep worldGenStep; @@ -25,11 +27,11 @@ public class BaseMetaData - public BaseMetaData(DhSectionPos pos, int checksum, byte dataLevel, EDhApiWorldGenerationStep worldGenStep, long dataTypeId, byte binaryDataFormatVersion) + public BaseMetaData(DhSectionPos pos, int checksum, byte dataLevel, EDhApiWorldGenerationStep worldGenStep, long dataTypeId, byte binaryDataFormatVersion, long dataVersion) { this.pos = pos; this.checksum = checksum; -// this.dataVersion = new AtomicLong(dataVersion); + this.dataVersion = new AtomicLong(dataVersion); this.dataLevel = dataLevel; this.worldGenStep = worldGenStep; 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 7362e7df4..f2ce48abf 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 @@ -223,6 +223,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements else { if (!doTriggerUpdate) return CompletableFuture.completedFuture(cachedRenderDataSource); + // Make a new future, and CAS it, or return the existing future CompletableFuture newFuture = new CompletableFuture<>(); CompletableFuture cas = AtomicsUtil.compareAndExchange(renderSourceLoadFutureRef, null, newFuture); @@ -334,7 +335,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements private BaseMetaData makeMetaData(ColumnRenderSource renderSource) { return new BaseMetaData(renderSource.getSectionPos(), -1, - renderSource.getDataDetail(), renderSource.worldGenStep, RenderSourceFileHandler.RENDER_SOURCE_TYPE_ID, renderSource.getRenderDataFormatVersion()); + renderSource.getDataDetail(), renderSource.worldGenStep, RenderSourceFileHandler.RENDER_SOURCE_TYPE_ID, renderSource.getRenderDataFormatVersion(), Long.MAX_VALUE); } private FileInputStream getFileInputStream() throws IOException 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 8e3613e34..edbb8f651 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 @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.file.renderfile; import com.google.common.collect.HashMultimap; 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.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.ClientLevelModule; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -18,6 +19,7 @@ import com.seibel.distanthorizons.core.util.FileScanUtil; 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 org.apache.logging.log4j.Logger; @@ -35,6 +37,8 @@ import static com.seibel.distanthorizons.core.util.FileScanUtil.RENDER_FILE_POST public class RenderSourceFileHandler implements ILodRenderSourceProvider { public static final boolean USE_LAZY_LOADING = true; + public static final boolean ALWAYS_INVALIDATE_CACHE = false; + public static final long RENDER_SOURCE_TYPE_ID = ColumnRenderSource.TYPE_ID; private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -441,12 +445,23 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { DebugRenderer.BoxWithLife box = 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.checksum == file.baseMetaData.dataVersion.get()) { + LOGGER.info("Skipping render cache update for {}", file.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) -> { @@ -468,6 +483,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { try { + file.baseMetaData.dataVersion.set(targetChecksumVersion.value); this.writeRenderSourceToFile(renderSource, file, newRenderSource); } catch (Throwable e) @@ -522,7 +538,6 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } currentRenderSource.updateFromRenderSource(newRenderSource); - currentRenderSource.localVersion.incrementAndGet(); //file.metaData.dataVersion.set(newDataVersion); file.baseMetaData.dataLevel = currentRenderSource.getDataDetail(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index b743a07e7..de9a89ca3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -115,7 +115,7 @@ public class LodRenderSection implements IDebugRenderable if (!this.isRenderingEnabled) { // this only needs to be called when first enabling the section - this.markBufferDirty(); + //this.markBufferDirty(); } this.isRenderingEnabled = true; @@ -284,7 +284,7 @@ public class LodRenderSection implements IDebugRenderable /** @return true if this section is loaded and set to render */ public boolean canBuildBuffer() { return this.renderSource != null && this.buildRenderBufferFuture == null && !this.renderSource.isEmpty() && this.isBufferOutdated(); } - private boolean isBufferOutdated() { return this.neighborUpdated || (this.renderSource.localVersion.get() - this.lastSwapLocalVersion) > 0; } + private boolean isBufferOutdated() { return this.neighborUpdated || this.renderSource.localVersion.get() != this.lastSwapLocalVersion; } /** @return true if this section is loaded and set to render */ public boolean canSwapBuffer() { return this.buildRenderBufferFuture != null && this.buildRenderBufferFuture.isDone(); }