diff --git a/src/main/java/com/seibel/lod/core/a7/datatype/LodDataSource.java b/src/main/java/com/seibel/lod/core/a7/datatype/LodDataSource.java index f054c08c0..c5b2f9b96 100644 --- a/src/main/java/com/seibel/lod/core/a7/datatype/LodDataSource.java +++ b/src/main/java/com/seibel/lod/core/a7/datatype/LodDataSource.java @@ -17,7 +17,7 @@ public interface LodDataSource { void setLocalVersion(int localVer); byte getDataVersion(); - void update(DHChunkPos chunkPos, ChunkSizedData data); + void update(ChunkSizedData data); // Saving related void saveData(ILevel level, DataMetaFile file, OutputStream dataStream) throws IOException; diff --git a/src/main/java/com/seibel/lod/core/a7/datatype/full/ChunkSizedData.java b/src/main/java/com/seibel/lod/core/a7/datatype/full/ChunkSizedData.java index 277203622..91befa945 100644 --- a/src/main/java/com/seibel/lod/core/a7/datatype/full/ChunkSizedData.java +++ b/src/main/java/com/seibel/lod/core/a7/datatype/full/ChunkSizedData.java @@ -3,8 +3,14 @@ package com.seibel.lod.core.a7.datatype.full; import com.seibel.lod.core.a7.datatype.full.accessor.FullArrayView; public class ChunkSizedData extends FullArrayView { - public ChunkSizedData() { + public final byte dataDetail; + public final int minX; + public final int minZ; + public ChunkSizedData(byte dataDetail, int minX, int minZ) { super(new IdBiomeBlockStateMap(), new long[16*16][0], 16); + this.dataDetail = dataDetail; + this.minX = minX; + this.minZ = minZ; } public void setSingleColumn(long[] data, int x, int z) { diff --git a/src/main/java/com/seibel/lod/core/a7/datatype/full/FullDataSource.java b/src/main/java/com/seibel/lod/core/a7/datatype/full/FullDataSource.java index 90b6a3923..e9c405e63 100644 --- a/src/main/java/com/seibel/lod/core/a7/datatype/full/FullDataSource.java +++ b/src/main/java/com/seibel/lod/core/a7/datatype/full/FullDataSource.java @@ -9,6 +9,7 @@ import com.seibel.lod.core.a7.datatype.LodDataSource; import com.seibel.lod.core.a7.util.IdMappingUtil; import com.seibel.lod.core.a7.pos.DhSectionPos; import com.seibel.lod.core.objects.DHChunkPos; +import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import java.io.DataOutputStream; @@ -49,9 +50,13 @@ public class FullDataSource extends FullArrayView implements LodDataSource { // } @Override - public void update(DHChunkPos chunkPos, ChunkSizedData data) { - if (getDataDetail() == 0) { - DhBlockPos2D blockOffset = chunkPos.getMinBlockPos().subtract(sectionPos.getSectionBBoxPos().getCorner()); + public void update(ChunkSizedData data) { + if (getDataDetail() == 0 && data.dataDetail == 0) { + DhBlockPos2D chunkBlockPos = new DhBlockPos2D(data.minX * 16, data.minZ * 16); + DhBlockPos2D blockOffset = chunkBlockPos.subtract(sectionPos.getCorner().getCorner()); + LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < SECTION_SIZE && blockOffset.z >= 0 && blockOffset.z < SECTION_SIZE, + "ChunkWrite of {} outside section {}. (cal offset {} larger than {})", + new DHChunkPos(data.minX, data.minZ), sectionPos, blockOffset, SECTION_SIZE); data.shadowCopyTo(this.subView(16, blockOffset.x, blockOffset.z)); } else { //TODO; @@ -65,4 +70,8 @@ public class FullDataSource extends FullArrayView implements LodDataSource { // dos.writeInt(size); } } + + public static FullDataSource createEmpty(DhSectionPos pos) { + return new FullDataSource(pos); + } } diff --git a/src/main/java/com/seibel/lod/core/a7/datatype/transform/LodDataBuilder.java b/src/main/java/com/seibel/lod/core/a7/datatype/transform/LodDataBuilder.java index 6daee1dc6..eaac174ac 100644 --- a/src/main/java/com/seibel/lod/core/a7/datatype/transform/LodDataBuilder.java +++ b/src/main/java/com/seibel/lod/core/a7/datatype/transform/LodDataBuilder.java @@ -14,7 +14,7 @@ public class LodDataBuilder { public static ChunkSizedData createChunkData(IChunkWrapper chunk) { if (!canGenerateLodFromChunk(chunk)) return null; - ChunkSizedData chunkData = new ChunkSizedData(); + ChunkSizedData chunkData = new ChunkSizedData((byte)0, chunk.getChunkPos().x, chunk.getChunkPos().z); for (int x=0; x<16; x++) { for (int z=0; z<16; z++) { diff --git a/src/main/java/com/seibel/lod/core/a7/generation/GenerationQueue.java b/src/main/java/com/seibel/lod/core/a7/generation/GenerationQueue.java index 4172ba7b0..e1f109028 100644 --- a/src/main/java/com/seibel/lod/core/a7/generation/GenerationQueue.java +++ b/src/main/java/com/seibel/lod/core/a7/generation/GenerationQueue.java @@ -126,14 +126,18 @@ public class GenerationQueue implements PlaceHolderQueue { + data.gridSize + " but requested granularity was " + granularity + " (equals to chunks of : " + (1 << (granularity-4)) + ") @ " + chunkPosMin); + logger.info("Writing chunk {} to {} with data detail {}", + chunkPosMin, new DHChunkPos(chunkPosMin.x + (1 << (granularity-4)), chunkPosMin.z + (1 << (granularity-4))), + dataDetail); + final byte sectionDetail = (byte) (dataDetail + FullDataSource.SECTION_SIZE_OFFSET); data.forEachPos((x,z) -> { ChunkSizedData chunkData = data.get(x,z); DhLodPos chunkDataPos = new DhLodPos((byte) (dataDetail + 4), x, z).convertUpwardsTo(sectionDetail); DhSectionPos sectionPos = new DhSectionPos(chunkDataPos.detail, chunkDataPos.x, chunkDataPos.z); - logger.info("Writing chunk {} with data detail {} to section {}", - new DHChunkPos(x+chunkPosMin.x,z+chunkPosMin.z), - dataDetail, sectionPos); + //logger.info("Writing chunk {} with data detail {} to section {}", + // new DHChunkPos(x+chunkPosMin.x,z+chunkPosMin.z), + // dataDetail, sectionPos); write(sectionPos, chunkData); }); }); diff --git a/src/main/java/com/seibel/lod/core/a7/pos/DhBlockPos2D.java b/src/main/java/com/seibel/lod/core/a7/pos/DhBlockPos2D.java index 7e1305f9f..e17c27159 100644 --- a/src/main/java/com/seibel/lod/core/a7/pos/DhBlockPos2D.java +++ b/src/main/java/com/seibel/lod/core/a7/pos/DhBlockPos2D.java @@ -4,6 +4,8 @@ import com.seibel.lod.core.objects.DHBlockPos; import com.seibel.lod.core.objects.Pos2D; import com.seibel.lod.core.util.LodUtil; +import java.util.Objects; + public class DhBlockPos2D { public final int x; public final int z; @@ -37,4 +39,23 @@ public class DhBlockPos2D { public static DhBlockPos2D fromPos2D(Pos2D pos) { return new DhBlockPos2D(pos.x, pos.y); } + + @Override + public String toString() { + return "(" + x + ", " + z + ")"; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DhBlockPos2D) { + DhBlockPos2D other = (DhBlockPos2D)obj; + return x == other.x && z == other.z; + } + return false; + } + + @Override + public int hashCode() { + return Integer.hashCode(x) ^ Integer.hashCode(z); + } } diff --git a/src/main/java/com/seibel/lod/core/a7/pos/DhSectionPos.java b/src/main/java/com/seibel/lod/core/a7/pos/DhSectionPos.java index ff01439cc..3f3efc038 100644 --- a/src/main/java/com/seibel/lod/core/a7/pos/DhSectionPos.java +++ b/src/main/java/com/seibel/lod/core/a7/pos/DhSectionPos.java @@ -95,6 +95,13 @@ public class DhSectionPos { sectionZ == that.sectionZ; } + @Override + public int hashCode() { + return Integer.hashCode(sectionDetail) ^ + Integer.hashCode(sectionX) ^ + Integer.hashCode(sectionZ); + } + // Serialize() is different from toString() as this requires it to NEVER be changed, and should be in a short format public String serialize() { return "[" + sectionDetail + ',' + sectionX + ',' + sectionZ + ']'; diff --git a/src/main/java/com/seibel/lod/core/a7/render/LodQuadTree.java b/src/main/java/com/seibel/lod/core/a7/render/LodQuadTree.java index b1da1d602..ca5a6210c 100644 --- a/src/main/java/com/seibel/lod/core/a7/render/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/core/a7/render/LodQuadTree.java @@ -383,29 +383,30 @@ public class LodQuadTree { if (section == null) return; // Cascade layers - if (doCascade && section.childCount == 0) { - LodUtil.assertTrue(childRingList != null); - // Create childs to cascade the layer. - for (byte i = 0; i < 4; i++) { - DhSectionPos childPos = section.pos.getChild(i); - LodRenderSection child = childRingList.get(childPos.sectionX, childPos.sectionZ); - if (child == null) { - child = childRingList.setChained(childPos.sectionX, childPos.sectionZ, - new LodRenderSection(childPos)); - child.childCount = 0; - } else { - LodUtil.assertTrue(child.childCount == -1, - "Self has child count 0 but an existing child's child count != -1!"); - child.childCount = 0; - } - } - section.childCount = 4; - } +// if (doCascade && section.childCount == 0) { +// LodUtil.assertTrue(childRingList != null); +// // Create childs to cascade the layer. +// for (byte i = 0; i < 4; i++) { +// DhSectionPos childPos = section.pos.getChild(i); +// LodRenderSection child = childRingList.get(childPos.sectionX, childPos.sectionZ); +// if (child == null) { +// child = childRingList.setChained(childPos.sectionX, childPos.sectionZ, +// new LodRenderSection(childPos)); +// child.childCount = 0; +// } else { +// LodUtil.assertTrue(child.childCount == -1, +// "Self has child count 0 but an existing child's child count != -1!"); +// child.childCount = 0; +// } +// } +// section.childCount = 4; +// } // Call load on new sections, and tick on existing ones, and dispose old sections if (section.childCount == -1) { ringList.set(pos.x, pos.y, null); section.dispose(); + return; } else { if (!section.isLoaded() && !section.isLoading()) { section.load(renderSourceProvider); @@ -418,18 +419,24 @@ public class LodQuadTree { } // Assertion steps - LodUtil.assertTrue(section.childCount == 4 || section.childCount == 0 || section.childCount == -1); + LodUtil.assertTrue(section.childCount == 4 || section.childCount == 0); if (section.pos.sectionDetail == LAYER_BEGINNING_OFFSET) LodUtil.assertTrue(section.childCount == 0); if (section.childCount == 4) LodUtil.assertTrue( getChildSection(section.pos, 0) != null && getChildSection(section.pos, 1) != null && getChildSection(section.pos, 2) != null && - getChildSection(section.pos, 3) != null); + getChildSection(section.pos, 3) != null, + "Sect {} child count 4 but childs have null: {} {} {} {}", + section.pos, getChildSection(section.pos, 0), getChildSection(section.pos, 1), + getChildSection(section.pos, 2), getChildSection(section.pos, 3)); if (section.childCount == 0 && section.pos.sectionDetail > LAYER_BEGINNING_OFFSET) LodUtil.assertTrue( getChildSection(section.pos, 0) == null && getChildSection(section.pos, 1) == null && getChildSection(section.pos, 2) == null && - getChildSection(section.pos, 3) == null); + getChildSection(section.pos, 3) == null, + "Sect {} child count 0 but childs are not null: {} {} {} {}", + section.pos, getChildSection(section.pos, 0), getChildSection(section.pos, 1), + getChildSection(section.pos, 2), getChildSection(section.pos, 3)); if (section.childCount == -1 && section.pos.sectionDetail < numbersOfSectionLevels-1) LodUtil.assertTrue( getParentSection(section.pos).childCount == 0); }); diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java b/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java index e5dcdb533..8749466cf 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/MetaFile.java @@ -8,12 +8,10 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; -import com.seibel.lod.core.a7.datatype.DataSourceLoader; import com.seibel.lod.core.a7.pos.DhSectionPos; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.util.LodUtil; @@ -56,7 +54,7 @@ public class MetaFile { // Load a metaFile in this path. It also automatically read the metadata. protected MetaFile(File path) throws IOException { - validatePath(); + validateFile(); try (FileInputStream fin = new FileInputStream(path)) { MappedByteBuffer buffer = fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE); this.path = path; @@ -86,9 +84,7 @@ public class MetaFile { this.pos = pos; } - protected void save() {} //TODO: Implement - - private void validatePath() throws IOException { + private void validateFile() throws IOException { if (!path.exists()) throw new IOException("File missing"); if (!path.isFile()) throw new IOException("Not a file"); if (!path.canRead()) throw new IOException("File not readable"); @@ -96,7 +92,7 @@ public class MetaFile { } protected void updateMetaData() throws IOException { - validatePath(); + validateFile(); try (FileChannel channel = FileChannel.open(path.toPath(), StandardOpenOption.READ)) { MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE); int magic = buffer.getInt(); @@ -124,7 +120,7 @@ public class MetaFile { } protected void writeData(Consumer dataWriter) throws IOException { - validatePath(); + if (path.exists()) validateFile(); File tempFile = File.createTempFile("", "tmp", path.getParentFile()); tempFile.deleteOnExit(); try (FileChannel file = FileChannel.open(tempFile.toPath(), diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java b/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java index c9cc635df..380edab53 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/file/DataMetaFile.java @@ -13,6 +13,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import com.seibel.lod.core.a7.datatype.LodDataSource; import com.seibel.lod.core.a7.datatype.DataSourceLoader; import com.seibel.lod.core.a7.datatype.full.ChunkSizedData; +import com.seibel.lod.core.a7.datatype.full.FullDataSource; import com.seibel.lod.core.a7.datatype.full.FullFormat; import com.seibel.lod.core.a7.save.io.MetaFile; import com.seibel.lod.core.a7.level.ILevel; @@ -151,7 +152,7 @@ public class DataMetaFile extends MetaFile { private LodDataSource loadAndUpdateDataSource() { LodDataSource data = loadFile(); - if (data == null) return null; + if (data == null) data = FullDataSource.createEmpty(pos); // Poll the write queue // First check if write queue is empty, then swap the write queue. @@ -161,8 +162,11 @@ public class DataMetaFile extends MetaFile { if (!isEmpty) { localVer = localVersion.incrementAndGet(); swapWriteQueue(); - // TODO: Use _backQueue to apply the changes into the data. + for (ChunkSizedData chunk : _backQueue.queue) { + data.update(chunk); + } write(data); + LOGGER.info("Updated Data file at {} for sect {}", path, pos); } else localVer = localVersion.get(); data.setLocalVersion(localVer); // Finally, return the data. @@ -170,6 +174,7 @@ public class DataMetaFile extends MetaFile { } private LodDataSource loadFile() { + if (!path.exists()) return null; // Refresh the metadata. try { super.updateMetaData(); diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/file/LocalDataFileHandler.java b/src/main/java/com/seibel/lod/core/a7/save/io/file/LocalDataFileHandler.java index 0ac1d7612..d750ed879 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/file/LocalDataFileHandler.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/file/LocalDataFileHandler.java @@ -1,8 +1,10 @@ package com.seibel.lod.core.a7.save.io.file; import com.google.common.collect.HashMultimap; +import com.seibel.lod.core.a7.datatype.DataSourceLoader; import com.seibel.lod.core.a7.datatype.LodDataSource; import com.seibel.lod.core.a7.datatype.full.ChunkSizedData; +import com.seibel.lod.core.a7.datatype.full.FullDataSource; import com.seibel.lod.core.a7.datatype.full.FullFormat; import com.seibel.lod.core.a7.level.IServerLevel; import com.seibel.lod.core.a7.pos.DhSectionPos; @@ -117,8 +119,9 @@ public class LocalDataFileHandler implements IDataSourceProvider { return; } // Slow path: if there is no file for this section, create one. - - DataMetaFile newMetaFile = new DataMetaFile(level, saveDir, sectionPos); + File file = computeDefaultFilePath(sectionPos); + DataMetaFile newMetaFile = new DataMetaFile(level, file, sectionPos); + LOGGER.info("Created new Data file at {} for sect {}", newMetaFile.path, sectionPos); // We add to the queue first so on CAS onto the map, no other thread // will see the new file without our write entry. diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderFileHandler.java b/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderFileHandler.java index b00e729e6..a176966c6 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderFileHandler.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderFileHandler.java @@ -109,6 +109,7 @@ public class RenderFileHandler implements IRenderSourceProvider { dataSourceProvider::isCacheValid, dataSourceProvider::read, level, computeDefaultFilePath(p), p)); + return metaFile.loadOrGetCached(renderCacheThread).handle( (render, e) -> { if (e != null) { diff --git a/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderMetaFile.java b/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderMetaFile.java index dfc741181..4fff7970a 100644 --- a/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderMetaFile.java +++ b/src/main/java/com/seibel/lod/core/a7/save/io/render/RenderMetaFile.java @@ -150,7 +150,8 @@ public class RenderMetaFile extends MetaFile { } } catch (IOException e) { LOGGER.warn("Failed to read render cache at {}:", path, e); - LOGGER.warn("Will ignore cache file."); + LOGGER.warn("Will delete cache file."); + path.delete(); } } // Otherwise, re-query and make the RenderSource diff --git a/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java b/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java new file mode 100644 index 000000000..4ae3bc9ac --- /dev/null +++ b/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java @@ -0,0 +1,45 @@ +package com.seibel.lod.core.logging.f3; + +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView; + +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Supplier; + +public class F3Screen { + public static boolean renderCustomF3 = true; + + private static final String[] DEFAULT_STR = { + "", + ModInfo.READABLE_NAME + " version: " + ModInfo.VERSION + }; + private static final LinkedList> selfUpdateMessages = new LinkedList<>(); + public static void addStringToDisplay(List list) { + list.addAll(Arrays.asList(DEFAULT_STR)); + Iterator> it = selfUpdateMessages.iterator(); + while (it.hasNext()) { + WeakReference ref = it.next(); + SelfUpdateMessage msg = ref.get(); + if (msg == null) { + it.remove(); + } else { + msg.print(list); + } + } + } + + public static class SelfUpdateMessage { + private final Supplier supplier; + public SelfUpdateMessage(Supplier message) { + selfUpdateMessages.add(new WeakReference<>(this)); + this.supplier = message; + } + public void print(List list) { + list.add(supplier.get()); + } + } +} diff --git a/src/main/java/com/seibel/lod/core/render/F3Screen.java b/src/main/java/com/seibel/lod/core/render/F3Screen.java deleted file mode 100644 index 9793a277f..000000000 --- a/src/main/java/com/seibel/lod/core/render/F3Screen.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.seibel.lod.core.render; - -import com.seibel.lod.core.ModInfo; - -import java.util.Arrays; -import java.util.List; - -public class F3Screen { - public static List f3List = Arrays.asList( - "", - ModInfo.READABLE_NAME + " version: " + ModInfo.VERSION - ); - public static boolean renderCustomF3 = false; -}