diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java new file mode 100644 index 000000000..acf2f8181 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGlProfileMode.java @@ -0,0 +1,30 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.enums.config; + +/** + * @since API 1.0.0 + */ +public enum EGlProfileMode +{ + CORE, + COMPAT, + ANY; +} diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/BitShiftUtil.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/BitShiftUtil.java index 9ad2e34f8..8bd3b2512 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/BitShiftUtil.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/BitShiftUtil.java @@ -30,7 +30,7 @@ public class BitShiftUtil { /** * Equivalent to:
- * {@literal 1 << value0, }
+ * {@literal 1 << value, }
* 2^value,
* Math.pow(2, value)

* diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java index 87d6db72f..6aad47dc6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core; +import com.seibel.distanthorizons.core.file.DataSourceReferenceTracker; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.core.world.DhApiWorldProxy; import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig; @@ -67,6 +68,8 @@ public class Initializer DhApi.Delayed.worldProxy = DhApiWorldProxy.INSTANCE; DhApi.Delayed.renderProxy = DhApiRenderProxy.INSTANCE; + DataSourceReferenceTracker.startGarbageCollectorBackgroundThread(); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 62d552d73..3d1821bc1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1176,21 +1176,35 @@ public class Config + "") .build(); + // TODO temporary test, remove me public static ConfigEntry skipChunkLoadUpdates = new ConfigEntry.Builder() .set(false) .comment("") .build(); + // TODO temporary test, remove me public static ConfigEntry skipChunkUnloadUpdates = new ConfigEntry.Builder() .set(false) .comment("") .build(); + // TODO temporary test, remove me public static ConfigEntry skipFullDataUpdateQueue = new ConfigEntry.Builder() .set(false) .comment("") .build(); + // TODO temporary test, remove me + public static ConfigEntry glProfileMode = new ConfigEntry.Builder() + .set(EGlProfileMode.CORE) + .comment("") + .build(); + // TODO temporary test, remove me + public static ConfigEntry glForwardCompatibilityMode = new ConfigEntry.Builder() + .set(true) + .comment("") + .build(); + 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 513f2594c..3b333bf4a 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 @@ -47,15 +47,17 @@ public class FullDataDownSampler ArrayList> futures; DhLodPos basePos = target.getSectionPos().getSectionBBoxPos().getCornerLodPos(CompleteFullDataSource.SECTION_SIZE_OFFSET); + + if (sectionSizeNeeded <= CompleteFullDataSource.SECTION_SIZE_OFFSET) { futures = new ArrayList<>(sectionSizeNeeded * sectionSizeNeeded); - for (int ox = 0; ox < sectionSizeNeeded; ox++) + for (int xOffset = 0; xOffset < sectionSizeNeeded; xOffset++) { - for (int oz = 0; oz < sectionSizeNeeded; oz++) + for (int zOffset = 0; zOffset < sectionSizeNeeded; zOffset++) { CompletableFuture future = provider.readAsync(new DhSectionPos( - CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + ox, basePos.z + oz)); + CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + xOffset, basePos.z + zOffset)); future = future.whenComplete((source, ex) -> { if (ex == null && source != null && source instanceof CompleteFullDataSource) { @@ -74,12 +76,12 @@ public class FullDataDownSampler { futures = new ArrayList<>(CompleteFullDataSource.WIDTH * CompleteFullDataSource.WIDTH); int multiplier = sectionSizeNeeded / CompleteFullDataSource.WIDTH; - for (int ox = 0; ox < CompleteFullDataSource.WIDTH; ox++) + for (int xOffset = 0; xOffset < CompleteFullDataSource.WIDTH; xOffset++) { - for (int oz = 0; oz < CompleteFullDataSource.WIDTH; oz++) + for (int zOffset = 0; zOffset < CompleteFullDataSource.WIDTH; zOffset++) { CompletableFuture future = provider.readAsync(new DhSectionPos( - CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + ox * multiplier, basePos.z + oz * multiplier)); + CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + xOffset * multiplier, basePos.z + zOffset * multiplier)); future = future.whenComplete((source, ex) -> { if (ex == null && source != null && source instanceof CompleteFullDataSource) { @@ -99,7 +101,7 @@ public class FullDataDownSampler public static void downSample(CompleteFullDataSource target, CompleteFullDataSource source) { - LodUtil.assertTrue(target.getSectionPos().overlaps(source.getSectionPos())); + LodUtil.assertTrue(target.getSectionPos().overlapsExactly(source.getSectionPos())); LodUtil.assertTrue(target.getDataDetailLevel() > source.getDataDetailLevel()); byte detailDiff = (byte) (target.getDataDetailLevel() - source.getDataDetailLevel()); @@ -111,11 +113,11 @@ public class FullDataDownSampler // The source occupies only 1 datapoint in the target // FIXME: TEMP method for down-sampling: take only the corner column int sourceSectionPerTargetData = 1 << (detailDiff - CompleteFullDataSource.SECTION_SIZE_OFFSET); - if (srcPos.sectionX % sourceSectionPerTargetData != 0 || srcPos.sectionZ % sourceSectionPerTargetData != 0) + if (srcPos.getX() % sourceSectionPerTargetData != 0 || srcPos.getZ() % sourceSectionPerTargetData != 0) { return; } - DhLodPos trgOffset = trgPos.getCorner(target.getDataDetailLevel()); + DhLodPos trgOffset = trgPos.getMinCornerLodPos(target.getDataDetailLevel()); DhLodPos srcOffset = srcPos.getSectionBBoxPos().convertToDetailLevel(target.getDataDetailLevel()); int offsetX = trgOffset.x - srcOffset.x; int offsetZ = trgOffset.z - srcOffset.z; @@ -131,7 +133,7 @@ public class FullDataDownSampler int srcDataPerTrgData = 1 << detailDiff; int overlappedTrgDataSize = CompleteFullDataSource.WIDTH / srcDataPerTrgData; - DhLodPos trgOffset = trgPos.getCorner(target.getDataDetailLevel()); + DhLodPos trgOffset = trgPos.getMinCornerLodPos(target.getDataDetailLevel()); DhLodPos srcOffset = srcPos.getSectionBBoxPos().getCornerLodPos(target.getDataDetailLevel()); int offsetX = trgOffset.x - srcOffset.x; int offsetZ = trgOffset.z - srcOffset.z; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java index 4c1135e44..881e1e5a5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java @@ -21,11 +21,9 @@ package com.seibel.distanthorizons.core.dataObjects.fullData.accessor; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; @@ -46,19 +44,22 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public final DhChunkPos pos; + public final DhChunkPos chunkPos; + public final DhSectionPos sectionPos; + // TODO replace this var with LodUtil.BLOCK_DETAIL_LEVEL public final byte detailLevel = LodUtil.BLOCK_DETAIL_LEVEL; - public ChunkSizedFullDataAccessor(DhChunkPos pos) + public ChunkSizedFullDataAccessor(DhChunkPos chunkPos) { - super(new FullDataPointIdMap(new DhSectionPos(pos)), + super(new FullDataPointIdMap(new DhSectionPos(chunkPos)), new long[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH][0], LodUtil.CHUNK_WIDTH); - this.pos = pos; + this.chunkPos = chunkPos; + this.sectionPos = new DhSectionPos(LodUtil.CHUNK_DETAIL_LEVEL, this.chunkPos.x, this.chunkPos.z); } @@ -237,9 +238,9 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor public long emptyCount() { return (LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH) - this.nonEmptyCount(); } - public DhLodPos getLodPos() { return new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, this.pos.x, this.pos.z); } + public DhSectionPos getSectionPos() { return this.sectionPos; } @Override - public String toString() { return this.pos + " " + this.nonEmptyCount(); } + public String toString() { return this.chunkPos + " " + this.nonEmptyCount(); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 4e151fe15..fad706b66 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -280,11 +280,11 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override public void update(ChunkSizedFullDataAccessor chunkDataView) { - LodUtil.assertTrue(this.sectionPos.getSectionBBoxPos().overlapsExactly(chunkDataView.getLodPos())); + LodUtil.assertTrue(this.sectionPos.overlapsExactly(chunkDataView.getSectionPos())); if (this.getDataDetailLevel() == LodUtil.BLOCK_DETAIL_LEVEL) { - DhBlockPos2D chunkBlockPos = new DhBlockPos2D(chunkDataView.pos.x * LodUtil.CHUNK_WIDTH, chunkDataView.pos.z * LodUtil.CHUNK_WIDTH); - DhBlockPos2D blockOffset = chunkBlockPos.subtract(this.sectionPos.getCorner().getCornerBlockPos()); + DhBlockPos2D chunkBlockPos = new DhBlockPos2D(chunkDataView.chunkPos.x * LodUtil.CHUNK_WIDTH, chunkDataView.chunkPos.z * LodUtil.CHUNK_WIDTH); + DhBlockPos2D blockOffset = chunkBlockPos.subtract(this.sectionPos.getMinCornerLodPos().getCornerBlockPos()); LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < WIDTH && blockOffset.z >= 0 && blockOffset.z < WIDTH); this.isEmpty = false; @@ -306,8 +306,8 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu { int dataPerFull = 1 << this.getDataDetailLevel(); int fullSize = LodUtil.CHUNK_WIDTH / dataPerFull; - DhLodPos dataOffset = chunkDataView.getLodPos().getCornerLodPos(this.getDataDetailLevel()); - DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetailLevel()); + DhLodPos dataOffset = chunkDataView.getSectionPos().getMinCornerLodPos(this.getDataDetailLevel()); + DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); int offsetX = dataOffset.x - baseOffset.x; int offsetZ = dataOffset.z - baseOffset.z; @@ -327,16 +327,16 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu { //FIXME: TEMPORARY int chunkPerFull = 1 << (this.getDataDetailLevel() - LodUtil.CHUNK_DETAIL_LEVEL); - if (chunkDataView.pos.x % chunkPerFull != 0 || chunkDataView.pos.z % chunkPerFull != 0) + if (chunkDataView.chunkPos.x % chunkPerFull != 0 || chunkDataView.chunkPos.z % chunkPerFull != 0) { return; } - DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetailLevel()); - DhLodPos dataOffset = chunkDataView.getLodPos().convertToDetailLevel(this.getDataDetailLevel()); + DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); + DhSectionPos dataOffset = chunkDataView.getSectionPos().convertNewToDetailLevel(this.getDataDetailLevel()); - int offsetX = dataOffset.x - baseOffset.x; - int offsetZ = dataOffset.z - baseOffset.z; + int offsetX = dataOffset.getX() - baseOffset.x; + int offsetZ = dataOffset.getZ() - baseOffset.z; LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH); this.isEmpty = false; @@ -359,18 +359,18 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu /** Returns whether data at the given posToWrite can effect the target region file at posToTest. */ public static boolean firstDataPosCanAffectSecond(DhSectionPos posToWrite, DhSectionPos posToTest) { - if (!posToWrite.overlaps(posToTest)) + if (!posToWrite.overlapsExactly(posToTest)) { // the testPosition is outside the writePosition return false; } - else if (posToTest.sectionDetailLevel > posToWrite.sectionDetailLevel) + else if (posToTest.getDetailLevel() > posToWrite.getDetailLevel()) { // the testPosition is larger (aka is less detailed) than the writePosition, // more detailed sections shouldn't be updated by lower detail sections return false; } - else if (posToWrite.sectionDetailLevel - posToTest.sectionDetailLevel <= SECTION_SIZE_OFFSET) + else if (posToWrite.getDetailLevel() - posToTest.getDetailLevel() <= SECTION_SIZE_OFFSET) { // if the difference in detail levels is very large, the posToWrite // may be skipped, due to how we sample large detail levels by only @@ -383,9 +383,9 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu { // the difference in detail levels is very large, // check if the posToWrite is in a corner of posToTest - byte sectPerData = (byte) BitShiftUtil.powerOfTwo(posToWrite.sectionDetailLevel - posToTest.sectionDetailLevel - SECTION_SIZE_OFFSET); + byte sectPerData = (byte) BitShiftUtil.powerOfTwo(posToWrite.getDetailLevel() - posToTest.getDetailLevel() - SECTION_SIZE_OFFSET); LodUtil.assertTrue(sectPerData != 0); - return posToTest.sectionX % sectPerData == 0 && posToTest.sectionZ % sectPerData == 0; + return posToTest.getX() % sectPerData == 0 && posToTest.getZ() % sectPerData == 0; } } @@ -419,7 +419,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override public DhSectionPos getSectionPos() { return this.sectionPos; } @Override - public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } + public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); } @Override public long getTypeId() { return TYPE_ID; } @@ -445,17 +445,17 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu public void updateFromLowerCompleteSource(CompleteFullDataSource subData) { - LodUtil.assertTrue(this.sectionPos.overlaps(subData.sectionPos)); - LodUtil.assertTrue(subData.sectionPos.sectionDetailLevel < this.sectionPos.sectionDetailLevel); + LodUtil.assertTrue(this.sectionPos.overlapsExactly(subData.sectionPos)); + LodUtil.assertTrue(subData.sectionPos.getDetailLevel() < this.sectionPos.getDetailLevel()); if (!firstDataPosCanAffectSecond(this.sectionPos, subData.sectionPos)) { return; } DhSectionPos lowerSectPos = subData.sectionPos; - byte detailDiff = (byte) (this.sectionPos.sectionDetailLevel - subData.sectionPos.sectionDetailLevel); + byte detailDiff = (byte) (this.sectionPos.getDetailLevel() - subData.sectionPos.getDetailLevel()); byte targetDataDetail = this.getDataDetailLevel(); - DhLodPos minDataPos = this.sectionPos.getCorner(targetDataDetail); + DhLodPos minDataPos = this.sectionPos.getMinCornerLodPos(targetDataDetail); if (detailDiff <= SECTION_SIZE_OFFSET) { int count = 1 << detailDiff; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index aac0d0b13..ab3388136 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -96,30 +96,30 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo public static HighDetailIncompleteFullDataSource createEmpty(DhSectionPos pos) { return new HighDetailIncompleteFullDataSource(pos); } private HighDetailIncompleteFullDataSource(DhSectionPos sectionPos) { - LodUtil.assertTrue(sectionPos.sectionDetailLevel > SPARSE_UNIT_DETAIL); - LodUtil.assertTrue(sectionPos.sectionDetailLevel <= MAX_SECTION_DETAIL); + LodUtil.assertTrue(sectionPos.getDetailLevel() > SPARSE_UNIT_DETAIL); + LodUtil.assertTrue(sectionPos.getDetailLevel() <= MAX_SECTION_DETAIL); this.sectionPos = sectionPos; - this.sectionCount = BitShiftUtil.powerOfTwo(sectionPos.sectionDetailLevel - SPARSE_UNIT_DETAIL); + this.sectionCount = BitShiftUtil.powerOfTwo(sectionPos.getDetailLevel() - SPARSE_UNIT_DETAIL); this.dataPointsPerSection = SECTION_SIZE / this.sectionCount; this.sparseData = new FullDataArrayAccessor[this.sectionCount * this.sectionCount]; - this.chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL); + this.chunkPos = sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); this.mapping = new FullDataPointIdMap(sectionPos); } protected HighDetailIncompleteFullDataSource(DhSectionPos sectionPos, FullDataPointIdMap mapping, FullDataArrayAccessor[] data) { - LodUtil.assertTrue(sectionPos.sectionDetailLevel > SPARSE_UNIT_DETAIL); - LodUtil.assertTrue(sectionPos.sectionDetailLevel <= MAX_SECTION_DETAIL); + LodUtil.assertTrue(sectionPos.getDetailLevel() > SPARSE_UNIT_DETAIL); + LodUtil.assertTrue(sectionPos.getDetailLevel() <= MAX_SECTION_DETAIL); this.sectionPos = sectionPos; - this.sectionCount = 1 << (byte) (sectionPos.sectionDetailLevel - SPARSE_UNIT_DETAIL); + this.sectionCount = 1 << (byte) (sectionPos.getDetailLevel() - SPARSE_UNIT_DETAIL); this.dataPointsPerSection = SECTION_SIZE / this.sectionCount; LodUtil.assertTrue(this.sectionCount * this.sectionCount == data.length); this.sparseData = data; - this.chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL); + this.chunkPos = sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); this.isEmpty = false; this.mapping = mapping; } @@ -144,8 +144,8 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override public FullDataSourceSummaryData readSourceSummaryInfo(FullDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException { - LodUtil.assertTrue(dataFile.pos.sectionDetailLevel > SPARSE_UNIT_DETAIL); - LodUtil.assertTrue(dataFile.pos.sectionDetailLevel <= MAX_SECTION_DETAIL); + LodUtil.assertTrue(dataFile.pos.getDetailLevel() > SPARSE_UNIT_DETAIL); + LodUtil.assertTrue(dataFile.pos.getDetailLevel() <= MAX_SECTION_DETAIL); int dataDetail = inputStream.readShort(); if (dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) @@ -255,7 +255,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo { // calculate the number of chunks and dataPoints based on the sparseDetail and sectionSize // TODO these values should be constant, should we still be calculating them like this? - int chunks = BitShiftUtil.powerOfTwo(dataFile.pos.sectionDetailLevel - SPARSE_UNIT_DETAIL); + int chunks = BitShiftUtil.powerOfTwo(dataFile.pos.getDetailLevel() - SPARSE_UNIT_DETAIL); int dataPointsPerChunk = SECTION_SIZE / chunks; @@ -422,7 +422,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override public DhSectionPos getSectionPos() { return this.sectionPos; } @Override - public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } + public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); } @Override public long getTypeId() { return TYPE_ID; } @@ -460,7 +460,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override public void update(ChunkSizedFullDataAccessor chunkDataView) { - int arrayOffset = this.calculateOffset(chunkDataView.pos.x, chunkDataView.pos.z); + int arrayOffset = this.calculateOffset(chunkDataView.chunkPos.x, chunkDataView.chunkPos.z); FullDataArrayAccessor newArray = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection); if (this.getDataDetailLevel() == chunkDataView.detailLevel) { @@ -492,8 +492,8 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo public void sampleFrom(IFullDataSource fullDataSource) { DhSectionPos pos = fullDataSource.getSectionPos(); - LodUtil.assertTrue(pos.sectionDetailLevel < this.sectionPos.sectionDetailLevel); - LodUtil.assertTrue(pos.overlaps(this.sectionPos)); + LodUtil.assertTrue(pos.getDetailLevel() < this.sectionPos.getDetailLevel()); + LodUtil.assertTrue(pos.overlapsExactly(this.sectionPos)); if (fullDataSource.isEmpty()) { return; @@ -524,10 +524,10 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo DhSectionPos pos = completeDataSource.getSectionPos(); this.isEmpty = false; - DhLodPos basePos = this.sectionPos.getCorner(SPARSE_UNIT_DETAIL); - DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL); + DhLodPos basePos = this.sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); + DhLodPos dataPos = pos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); - int coveredChunks = pos.getWidth(SPARSE_UNIT_DETAIL).numberOfLodSectionsWide; + int coveredChunks = pos.getWidthCountForLowerDetailedSection(SPARSE_UNIT_DETAIL); int sourceDataPerChunk = SPARSE_UNIT_SIZE >>> completeDataSource.getDataDetailLevel(); LodUtil.assertTrue((coveredChunks * sourceDataPerChunk) == CompleteFullDataSource.WIDTH); @@ -551,8 +551,8 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo DhSectionPos pos = sparseDataSource.getSectionPos(); this.isEmpty = false; - DhLodPos basePos = this.sectionPos.getCorner(SPARSE_UNIT_DETAIL); - DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL); + DhLodPos basePos = this.sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); + DhLodPos dataPos = pos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); int offsetX = dataPos.x - basePos.x; int offsetZ = dataPos.z - basePos.z; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index 8b3c327fa..9e9fa81d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -84,7 +84,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp private LowDetailIncompleteFullDataSource(DhSectionPos sectionPos) { super(new FullDataPointIdMap(sectionPos), new long[WIDTH * WIDTH][0], WIDTH); - LodUtil.assertTrue(sectionPos.sectionDetailLevel > HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL); + LodUtil.assertTrue(sectionPos.getDetailLevel() > HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL); this.sectionPos = sectionPos; this.isColumnNotEmpty = new BitSet(WIDTH * WIDTH); @@ -295,7 +295,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp @Override public DhSectionPos getSectionPos() { return this.sectionPos; } @Override - public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } + public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); } @Override public long getTypeId() { return TYPE_ID; } @Override @@ -320,21 +320,21 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp @Override public void update(ChunkSizedFullDataAccessor data) { - LodUtil.assertTrue(this.sectionPos.getSectionBBoxPos().overlapsExactly(data.getLodPos())); + LodUtil.assertTrue(this.sectionPos.overlapsExactly(data.getSectionPos())); if (this.getDataDetailLevel() >= 4) { //FIXME: TEMPORARY int chunkPerFull = 1 << (this.getDataDetailLevel() - 4); - if (data.pos.x % chunkPerFull != 0 || data.pos.z % chunkPerFull != 0) + if (data.chunkPos.x % chunkPerFull != 0 || data.chunkPos.z % chunkPerFull != 0) { return; } - DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetailLevel()); - DhLodPos dataOffset = data.getLodPos().convertToDetailLevel(this.getDataDetailLevel()); - int offsetX = dataOffset.x - baseOffset.x; - int offsetZ = dataOffset.z - baseOffset.z; + DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); + DhSectionPos dataOffset = data.getSectionPos().convertNewToDetailLevel(this.getDataDetailLevel()); + int offsetX = dataOffset.getX() - baseOffset.x; + int offsetZ = dataOffset.getZ() - baseOffset.z; LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH); this.isEmpty = false; @@ -355,8 +355,8 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp public void sampleFrom(IFullDataSource fullDataSource) { DhSectionPos pos = fullDataSource.getSectionPos(); - LodUtil.assertTrue(pos.sectionDetailLevel < this.sectionPos.sectionDetailLevel); - LodUtil.assertTrue(pos.overlaps(this.sectionPos)); + LodUtil.assertTrue(pos.getDetailLevel() < this.sectionPos.getDetailLevel()); + LodUtil.assertTrue(pos.overlapsExactly(this.sectionPos)); if (fullDataSource.isEmpty()) { @@ -386,21 +386,21 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp private void sampleFrom(HighDetailIncompleteFullDataSource sparseSource) { - DhLodPos thisLodPos = this.sectionPos.getCorner(this.getDataDetailLevel()); + DhLodPos thisLodPos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); DhSectionPos pos = sparseSource.getSectionPos(); this.isEmpty = false; - if (this.getDataDetailLevel() > this.sectionPos.sectionDetailLevel) + if (this.getDataDetailLevel() > this.sectionPos.getDetailLevel()) { - DhLodPos dataLodPos = pos.getCorner(this.getDataDetailLevel()); + DhLodPos dataLodPos = pos.getMinCornerLodPos(this.getDataDetailLevel()); int offsetX = dataLodPos.x - thisLodPos.x; int offsetZ = dataLodPos.z - thisLodPos.z; LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH); int chunksPerData = 1 << (this.getDataDetailLevel() - HighDetailIncompleteFullDataSource.SPARSE_UNIT_DETAIL); - int dataSpan = this.sectionPos.getWidth(this.getDataDetailLevel()).numberOfLodSectionsWide; + int dataSpan = this.sectionPos.getWidthCountForLowerDetailedSection(this.getDataDetailLevel()); for (int xOffset = 0; xOffset < dataSpan; xOffset++) { @@ -421,7 +421,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp else { DhLodPos dataLodPos = pos.getSectionBBoxPos(); - int lowerSectionsPerData = this.sectionPos.getWidth(dataLodPos.detailLevel).numberOfLodSectionsWide; + int lowerSectionsPerData = this.sectionPos.getWidthCountForLowerDetailedSection(dataLodPos.detailLevel); if (dataLodPos.x % lowerSectionsPerData != 0 || dataLodPos.z % lowerSectionsPerData != 0) { return; @@ -447,14 +447,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp this.isEmpty = false; this.downsampleFrom(completeSource); - if (this.getDataDetailLevel() > this.sectionPos.sectionDetailLevel) // TODO what does this mean? + if (this.getDataDetailLevel() > this.sectionPos.getDetailLevel()) // TODO what does this mean? { - DhLodPos thisLodPos = this.sectionPos.getCorner(this.getDataDetailLevel()); - DhLodPos dataLodPos = pos.getCorner(this.getDataDetailLevel()); + DhLodPos thisLodPos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); + DhLodPos dataLodPos = pos.getMinCornerLodPos(this.getDataDetailLevel()); int offsetX = dataLodPos.x - thisLodPos.x; int offsetZ = dataLodPos.z - thisLodPos.z; - int dataWidth = this.sectionPos.getWidth(this.getDataDetailLevel()).numberOfLodSectionsWide; + int dataWidth = this.sectionPos.getWidthCountForLowerDetailedSection(this.getDataDetailLevel()); for (int xOffset = 0; xOffset < dataWidth; xOffset++) { @@ -467,14 +467,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp else { DhLodPos dataPos = pos.getSectionBBoxPos(); - int lowerSectionsPerData = this.sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide; + int lowerSectionsPerData = this.sectionPos.getWidthCountForLowerDetailedSection(dataPos.detailLevel); if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) { return; } - DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetailLevel()); + DhLodPos basePos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); dataPos = dataPos.convertToDetailLevel(this.getDataDetailLevel()); int offsetX = dataPos.x - basePos.x; int offsetZ = dataPos.z - basePos.z; @@ -490,14 +490,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp this.downsampleFrom(spottySource); - if (this.getDataDetailLevel() > this.sectionPos.sectionDetailLevel) + if (this.getDataDetailLevel() > this.sectionPos.getDetailLevel()) { - DhLodPos thisLodPos = this.sectionPos.getCorner(this.getDataDetailLevel()); - DhLodPos dataLodPos = pos.getCorner(this.getDataDetailLevel()); + DhLodPos thisLodPos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); + DhLodPos dataLodPos = pos.getMinCornerLodPos(this.getDataDetailLevel()); int offsetX = dataLodPos.x - thisLodPos.x; int offsetZ = dataLodPos.z - thisLodPos.z; - int dataWidth = this.sectionPos.getWidth(this.getDataDetailLevel()).numberOfLodSectionsWide; + int dataWidth = this.sectionPos.getWidthCountForLowerDetailedSection(this.getDataDetailLevel()); for (int xOffset = 0; xOffset < dataWidth; xOffset++) { @@ -510,14 +510,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp else { DhLodPos dataPos = pos.getSectionBBoxPos(); - int lowerSectionsPerData = this.sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide; + int lowerSectionsPerData = this.sectionPos.getWidthCountForLowerDetailedSection(dataPos.detailLevel); if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) { return; } - DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetailLevel()); + DhLodPos basePos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); dataPos = dataPos.convertToDetailLevel(this.getDataDetailLevel()); int offsetX = dataPos.x - basePos.x; int offsetZ = dataPos.z - basePos.z; @@ -573,14 +573,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp public static boolean neededForPosition(DhSectionPos posToWrite, DhSectionPos posToTest) { - if (!posToWrite.overlaps(posToTest)) + if (!posToWrite.overlapsExactly(posToTest)) return false; - if (posToTest.sectionDetailLevel > posToWrite.sectionDetailLevel) + if (posToTest.getDetailLevel() > posToWrite.getDetailLevel()) return false; - if (posToWrite.sectionDetailLevel - posToTest.sectionDetailLevel <= SECTION_SIZE_OFFSET) + if (posToWrite.getDetailLevel() - posToTest.getDetailLevel() <= SECTION_SIZE_OFFSET) return true; - byte sectPerData = (byte) (1 << (posToWrite.sectionDetailLevel - posToTest.sectionDetailLevel - SECTION_SIZE_OFFSET)); - return posToTest.sectionX % sectPerData == 0 && posToTest.sectionZ % sectPerData == 0; + byte sectPerData = (byte) (1 << (posToWrite.getDetailLevel() - posToTest.getDetailLevel() - SECTION_SIZE_OFFSET)); + return posToTest.getX() % sectPerData == 0 && posToTest.getZ() % sectPerData == 0; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java index bcf456b07..f0599441e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java @@ -104,7 +104,7 @@ public interface IFullDataSource while (posList.size() > 0) { DhSectionPos pos = posList.remove(); - if (pos.sectionDetailLevel > highestGeneratorDetailLevel) + if (pos.getDetailLevel() > highestGeneratorDetailLevel) { pos.forEachChild((childPos) -> { posList.push(childPos); }); } @@ -128,30 +128,30 @@ public interface IFullDataSource int sourceRelWidth = this.getWidthInDataPoints(); - if (quadrantPos.sectionDetailLevel < highestGeneratorDetailLevel) + if (quadrantPos.getDetailLevel() < highestGeneratorDetailLevel) { throw new IllegalArgumentException("detail level lower than world generator can accept."); } - else if (quadrantPos.sectionDetailLevel == highestGeneratorDetailLevel) + else if (quadrantPos.getDetailLevel() == highestGeneratorDetailLevel) { // we are at the highest detail level the world generator can accept, // we either need to generate this whole section, or not at all // TODO combine duplicate code - byte childDetailLevel = (byte) (quadrantPos.sectionDetailLevel); + byte childDetailLevel = (byte) (quadrantPos.getDetailLevel()); - int quadrantDetailLevelDiff = this.getSectionPos().sectionDetailLevel - childDetailLevel; + int quadrantDetailLevelDiff = this.getSectionPos().getDetailLevel() - childDetailLevel; int widthInSecPos = BitShiftUtil.powerOfTwo(quadrantDetailLevelDiff); int relWidthForSecPos = sourceRelWidth / widthInSecPos; - DhSectionPos minSecPos = this.getSectionPos().convertToDetailLevel(childDetailLevel); + DhSectionPos minSecPos = this.getSectionPos().convertNewToDetailLevel(childDetailLevel); DhSectionPos inputPos = quadrantPos; - int minRelX = inputPos.sectionX - minSecPos.sectionX; - int minRelZ = inputPos.sectionZ - minSecPos.sectionZ; + int minRelX = inputPos.getX() - minSecPos.getX(); + int minRelZ = inputPos.getZ() - minSecPos.getZ(); int maxRelX = minRelX + 1; int maxRelZ = minRelZ + 1; @@ -188,21 +188,21 @@ public interface IFullDataSource // TODO comment // TODO combine duplicate code - byte childDetailLevel = (byte) (quadrantPos.sectionDetailLevel - 1); + byte childDetailLevel = (byte) (quadrantPos.getDetailLevel() - 1); for (int i = 0; i < 4; i++) { - int quadrantDetailLevelDiff = this.getSectionPos().sectionDetailLevel - childDetailLevel; + int quadrantDetailLevelDiff = this.getSectionPos().getDetailLevel() - childDetailLevel; int widthInSecPos = BitShiftUtil.powerOfTwo(quadrantDetailLevelDiff); int relWidthForSecPos = sourceRelWidth / widthInSecPos; - DhSectionPos minSecPos = this.getSectionPos().convertToDetailLevel(childDetailLevel); + DhSectionPos minSecPos = this.getSectionPos().convertNewToDetailLevel(childDetailLevel); DhSectionPos inputPos = quadrantPos.getChildByIndex(i); - int minRelX = inputPos.sectionX - minSecPos.sectionX; - int minRelZ = inputPos.sectionZ - minSecPos.sectionZ; + int minRelX = inputPos.getX() - minSecPos.getX(); + int minRelZ = inputPos.getZ() - minSecPos.getZ(); int maxRelX = minRelX + 1; int maxRelZ = minRelZ + 1; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java index 013f964dc..45d34b8cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderLoader.java @@ -20,16 +20,10 @@ package com.seibel.distanthorizons.core.dataObjects.render; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; -import com.seibel.distanthorizons.core.file.renderfile.RenderMetaDataFile; -import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.file.renderfile.RenderDataMetaFile; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer; import org.apache.logging.log4j.Logger; import java.io.IOException; @@ -37,7 +31,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; /** - * Handles loading and parsing {@link RenderMetaDataFile}s to create {@link ColumnRenderSource}s.

+ * Handles loading and parsing {@link RenderDataMetaFile}s to create {@link ColumnRenderSource}s.

* * Please see the {@link ColumnRenderLoader#loadRenderSource} method to see what * file versions this class can handle. @@ -54,7 +48,7 @@ public class ColumnRenderLoader - public ColumnRenderSource loadRenderSource(RenderMetaDataFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException + public ColumnRenderSource loadRenderSource(RenderDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException { int dataFileVersion = dataFile.baseMetaData.binaryDataFormatVersion; 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 0fc4ea570..ece6027b7 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 @@ -109,7 +109,7 @@ public class ColumnRenderSource */ public ColumnRenderSource(DhSectionPos sectionPos, ColumnRenderLoader.ParsedColumnData parsedColumnData, IDhLevel level) throws IOException { - if (sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET != parsedColumnData.detailLevel) + if (sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET != parsedColumnData.detailLevel) { throw new IOException("Invalid data: detail level does not match"); } @@ -308,16 +308,16 @@ public class ColumnRenderSource */ public boolean updateWithChunkData(ChunkSizedFullDataAccessor chunkDataView, IDhClientLevel level) { - final String errorMessagePrefix = "Unable to complete fastWrite for RenderSource pos: [" + this.sectionPos + "] and chunk pos: [" + chunkDataView.pos + "]. Error:"; + final String errorMessagePrefix = "Unable to complete fastWrite for RenderSource pos: [" + this.sectionPos + "] and chunk pos: [" + chunkDataView.chunkPos + "]. Error:"; final DhSectionPos renderSourcePos = this.getSectionPos(); - final int sourceBlockX = renderSourcePos.getCorner().getCornerBlockPos().x; - final int sourceBlockZ = renderSourcePos.getCorner().getCornerBlockPos().z; + final int sourceBlockX = renderSourcePos.getMinCornerLodPos().getCornerBlockPos().x; + final int sourceBlockZ = renderSourcePos.getMinCornerLodPos().getCornerBlockPos().z; // offset between the incoming chunk data and this render source - final int blockOffsetX = (chunkDataView.pos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX; - final int blockOffsetZ = (chunkDataView.pos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ; + final int blockOffsetX = (chunkDataView.chunkPos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX; + final int blockOffsetZ = (chunkDataView.chunkPos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ; final int sourceDataPointBlockWidth = BitShiftUtil.powerOfTwo(this.getDataDetail()); @@ -360,27 +360,27 @@ public class ColumnRenderSource } this.fillDebugFlag(blockOffsetX, blockOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT); } - else if (chunkDataView.detailLevel < this.getDataDetail() && this.getDataDetail() <= chunkDataView.getLodPos().detailLevel) + else if (chunkDataView.detailLevel < this.getDataDetail() && this.getDataDetail() <= chunkDataView.getSectionPos().getDetailLevel()) { this.markNotEmpty(); // multiple chunk data points converting to 1 column data point - DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel); - DhLodPos sourceCornerPos = renderSourcePos.getCorner(this.getDataDetail()); + DhLodPos dataCornerPos = chunkDataView.getSectionPos().getMinCornerLodPos(chunkDataView.detailLevel); + DhLodPos sourceCornerPos = renderSourcePos.getMinCornerLodPos(this.getDataDetail()); DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(this.getDataDetail()); int relStartX = Math.floorMod(sourceStartingChangePos.x, this.getWidthInDataPoints()); int relStartZ = Math.floorMod(sourceStartingChangePos.z, this.getWidthInDataPoints()); int dataToSourceScale = sourceCornerPos.getWidthAtDetail(chunkDataView.detailLevel); - int columnsInChunk = chunkDataView.getLodPos().getWidthAtDetail(this.getDataDetail()); + int columnsInChunk = chunkDataView.getSectionPos().getWidthCountForLowerDetailedSection(this.getDataDetail()); - for (int ox = 0; ox < columnsInChunk; ox++) + for (int xOffset = 0; xOffset < columnsInChunk; xOffset++) { - for (int oz = 0; oz < columnsInChunk; oz++) + for (int zOffset = 0; zOffset < columnsInChunk; zOffset++) { - int relSourceX = relStartX + ox; - int relSourceZ = relStartZ + oz; + int relSourceX = relStartX + xOffset; + int relSourceZ = relStartZ + zOffset; ColumnArrayView columnArrayView = this.getVerticalDataPointView(relSourceX, relSourceZ); int hash = columnArrayView.getDataHash(); - SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(ox * dataToSourceScale, oz * dataToSourceScale); + SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(xOffset * dataToSourceScale, zOffset * dataToSourceScale); FullDataToRenderDataTransformer.convertColumnData(level, sourceBlockX + sourceDataPointBlockWidth * relSourceX, sourceBlockZ + sourceDataPointBlockWidth * relSourceZ, @@ -390,14 +390,14 @@ public class ColumnRenderSource } this.fillDebugFlag(relStartX, relStartZ, columnsInChunk, columnsInChunk, ColumnRenderSource.DebugSourceFlag.DIRECT); } - else if (chunkDataView.getLodPos().detailLevel < this.getDataDetail()) + else if (chunkDataView.getSectionPos().getDetailLevel() < this.getDataDetail()) { // The entire chunk is being converted to a single column data point, possibly. - DhLodPos dataCornerPos = chunkDataView.getLodPos().getCornerLodPos(chunkDataView.detailLevel); - DhLodPos sourceCornerPos = renderSourcePos.getCorner(this.getDataDetail()); + DhLodPos dataCornerPos = chunkDataView.getSectionPos().getMinCornerLodPos(chunkDataView.detailLevel); + DhLodPos sourceCornerPos = renderSourcePos.getMinCornerLodPos(this.getDataDetail()); DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(this.getDataDetail()); - int chunksPerColumn = sourceStartingChangePos.getWidthAtDetail(chunkDataView.getLodPos().detailLevel); - if (chunkDataView.getLodPos().x % chunksPerColumn != 0 || chunkDataView.getLodPos().z % chunksPerColumn != 0) + int chunksPerColumn = sourceStartingChangePos.getWidthAtDetail(chunkDataView.getSectionPos().getDetailLevel()); + if (chunkDataView.getSectionPos().getX() % chunksPerColumn != 0 || chunkDataView.getSectionPos().getZ() % chunksPerColumn != 0) { return false; // not a multiple of the column size, so no change } @@ -443,7 +443,7 @@ public class ColumnRenderSource public DhSectionPos getSectionPos() { return this.sectionPos; } - public byte getDataDetail() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } + public byte getDataDetail() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); } /** @return how many data points wide this {@link ColumnRenderSource} is. */ public int getWidthInDataPoints() { return BitShiftUtil.powerOfTwo(this.getDetailOffset()); } @@ -526,7 +526,7 @@ public class ColumnRenderSource stringBuilder.append(this.sectionPos); stringBuilder.append(LINE_DELIMITER); - int size = this.sectionPos.getWidth().numberOfLodSectionsWide; + int size = 1; for (int z = 0; z < size; z++) { for (int x = 0; x < size; x++) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 168c3e283..a5b25e158 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -125,7 +125,7 @@ public class ColumnRenderBufferBuilder if (buffer == null) { - buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getCorner().getCornerBlockPos(), clientLevel.getMinY()), renderSource.sectionPos); + buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getMinCornerLodPos().getCornerBlockPos(), clientLevel.getMinY()), renderSource.sectionPos); } try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 40e707746..1438996ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -132,12 +132,12 @@ public class FullDataToRenderDataTransformer if (dataDetail == columnSource.getDataDetail()) { - int baseX = pos.getCorner().getCornerBlockPos().x; - int baseZ = pos.getCorner().getCornerBlockPos().z; + int baseX = pos.getMinCornerLodPos().getCornerBlockPos().x; + int baseZ = pos.getMinCornerLodPos().getCornerBlockPos().z; - for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) + for (int x = 0; x < pos.getWidthCountForLowerDetailedSection(dataDetail); x++) { - for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) + for (int z = 0; z < pos.getWidthCountForLowerDetailedSection(dataDetail); z++) { throwIfThreadInterrupted(); @@ -182,11 +182,11 @@ public class FullDataToRenderDataTransformer if (dataDetail == columnSource.getDataDetail()) { - int baseX = pos.getCorner().getCornerBlockPos().x; - int baseZ = pos.getCorner().getCornerBlockPos().z; - for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) + int baseX = pos.getMinCornerLodPos().getCornerBlockPos().x; + int baseZ = pos.getMinCornerLodPos().getCornerBlockPos().z; + for (int x = 0; x < pos.getWidthCountForLowerDetailedSection(dataDetail); x++) { - for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) + for (int z = 0; z < pos.getWidthCountForLowerDetailedSection(dataDetail); z++) { throwIfThreadInterrupted(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourceReferenceTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourceReferenceTracker.java new file mode 100644 index 000000000..2e7f7d51a --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourceReferenceTracker.java @@ -0,0 +1,233 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.file; + +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; +import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; +import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; +import com.seibel.distanthorizons.core.file.renderfile.RenderDataMetaFile; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.ThreadUtil; +import org.apache.logging.log4j.Logger; + +import java.io.Closeable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Keeps track of {@link FullDataMetaFile} and {@link RenderDataMetaFile}'s + * and handles freeing their underlying data sources if they go unused for a certain amount of time. + */ +public class DataSourceReferenceTracker +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final boolean LOG_GARBAGE_COLLECTIONS = false; + + /** How often the garbage collector thread will run */ + private static final long MS_BETWEEN_GARBAGE_CHECKS = TimeUnit.SECONDS.toMillis(60); + /** How long a data source has to go unused before it can be freed */ + private static final long MS_TO_EXPIRE_DATA_SOURCE = TimeUnit.SECONDS.toMillis(60); + + + // these queues are populated by the JVM's garbage collector after the assigned soft reference is freed + private static final ReferenceQueue FULL_DATA_GARBAGE_COLLECTED_QUEUE = new ReferenceQueue<>(); + private static final ReferenceQueue RENDER_DATA_GARBAGE_COLLECTED_QUEUE = new ReferenceQueue<>(); + + // TODO using a ConcurrentHashMap may or may not be the best choice here + private static final Set FULL_DATA_SOFT_REFS = ConcurrentHashMap.newKeySet(); + private static final Set RENDER_DATA_SOFT_REFS = ConcurrentHashMap.newKeySet(); + + private static final ThreadPoolExecutor GARBAGE_COLLECTOR_THREAD = ThreadUtil.makeSingleThreadPool("DataSourceReferenceTracker", ThreadUtil.MINIMUM_RELATIVE_PRIORITY); + + + + //=================// + // collector logic // + //=================// + + /** Warning: this should not be called more than once. */ + public static void startGarbageCollectorBackgroundThread() { /*GARBAGE_COLLECTOR_THREAD.execute(() -> garbageCollectorLoop());*/ } + private static void garbageCollectorLoop() + { + while(true) + { + try + { + runGarbageCollection(); + Thread.sleep(MS_BETWEEN_GARBAGE_CHECKS); + } + catch (InterruptedException e) + { + LOGGER.error("Garbage collector thread interrupted.", e); + } + catch (Exception e) + { + LOGGER.error("Unexpected data source garbage collector exception: " + e.getMessage(), e); + } + } + } + + public static void runGarbageCollection() + { + removeGarbageCollectedDataSources(); + removeExpiredDataSources(); + } + private static void removeGarbageCollectedDataSources() + { + FullDataSourceSoftRef garbageCollectedFullDataSoftRef = (FullDataSourceSoftRef) FULL_DATA_GARBAGE_COLLECTED_QUEUE.poll(); + while (garbageCollectedFullDataSoftRef != null) + { + if (LOG_GARBAGE_COLLECTIONS) + { + LOGGER.info("Full Data at pos: " + garbageCollectedFullDataSoftRef.metaFile.pos + " has been soft released."); + } + garbageCollectedFullDataSoftRef.close(); + + garbageCollectedFullDataSoftRef = (FullDataSourceSoftRef) FULL_DATA_GARBAGE_COLLECTED_QUEUE.poll(); + } + + RenderDataSourceSoftRef renderSoftRef = (RenderDataSourceSoftRef) RENDER_DATA_GARBAGE_COLLECTED_QUEUE.poll(); + while (renderSoftRef != null) + { + if (LOG_GARBAGE_COLLECTIONS) + { + LOGGER.info("Render Data at pos: " + renderSoftRef.metaFile.pos + " has been soft released."); + } + renderSoftRef.close(); + + renderSoftRef = (RenderDataSourceSoftRef) RENDER_DATA_GARBAGE_COLLECTED_QUEUE.poll(); + } + } + private static void removeExpiredDataSources() + { + // TODO merge these loops + FULL_DATA_SOFT_REFS.removeIf((fullDataSoftRef) -> + { + boolean remove = fullDataSoftRef.isDataSourceExpired() || (fullDataSoftRef.silentGet() == null); + if (remove) + { + fullDataSoftRef.clear(); + fullDataSoftRef.close(); + + if (LOG_GARBAGE_COLLECTIONS) + { + LOGGER.info("Full Data at pos: " + fullDataSoftRef.metaFile.pos + " has expired and will be released at the next GC. ["+FULL_DATA_SOFT_REFS.size()+"] Full data sources remain."); + } + } + + return remove; + }); + + // TODO merge these loops + RENDER_DATA_SOFT_REFS.removeIf((renderDataSoftRef) -> + { + boolean remove = renderDataSoftRef.isDataSourceExpired() || (renderDataSoftRef.silentGet() == null); + if (remove) + { + renderDataSoftRef.clear(); + renderDataSoftRef.close(); + + if (LOG_GARBAGE_COLLECTIONS) + { + LOGGER.info("Render Data at pos: " + renderDataSoftRef.metaFile.pos + " has expired and will be released at the next GC. ["+RENDER_DATA_SOFT_REFS.size()+"] Render data sources remain."); + } + } + + return remove; + }); + } + + + + //================// + // helper classes // + //================// + + public static class FullDataSourceSoftRef extends AbstractDataSourceSoftTracker + { + public FullDataSourceSoftRef(FullDataMetaFile metaFile, IFullDataSource data) + { + super(metaFile, data, FULL_DATA_GARBAGE_COLLECTED_QUEUE); + FULL_DATA_SOFT_REFS.add(this); + } + + @Override + public void close() { FULL_DATA_SOFT_REFS.remove(this); } + } + public static class RenderDataSourceSoftRef extends AbstractDataSourceSoftTracker + { + public RenderDataSourceSoftRef(RenderDataMetaFile metaFile, ColumnRenderSource data) + { + super(metaFile, data, RENDER_DATA_GARBAGE_COLLECTED_QUEUE); + RENDER_DATA_SOFT_REFS.add(this); + } + + @Override + public void close() { RENDER_DATA_SOFT_REFS.remove(this); } + } + + /** wrapper for a {@link SoftReference} so we can track and manually remove unused sources */ + public static abstract class AbstractDataSourceSoftTracker extends SoftReference implements Closeable + { + public final TMetaFile metaFile; + public final long createdMsTime; + + private long expirationMsTime; + + + + public AbstractDataSourceSoftTracker(TMetaFile metaFile, TDataSource dataSource, ReferenceQueue referenceQueue) + { + super(dataSource, referenceQueue); + this.metaFile = metaFile; + + this.createdMsTime = System.currentTimeMillis(); + this.expirationMsTime = System.currentTimeMillis(); + } + + + + public void updateLastAccessedTime() { this.expirationMsTime = System.currentTimeMillis() + MS_TO_EXPIRE_DATA_SOURCE; } + public long getExpirationMsTime() { return this.expirationMsTime; } + public boolean isDataSourceExpired() { return this.expirationMsTime > System.currentTimeMillis(); } + + + @Override + public TDataSource get() + { + this.updateLastAccessedTime(); + return super.get(); + } + + /** + * Gets the underlying datasource without updating the {@link AbstractDataSourceSoftTracker#expirationMsTime} + * Note: this still updates {@link SoftReference}'s timestamp variable which may prevent the JVM from + * marking this reference as valid for deletion. + */ + public TDataSource silentGet() { return super.get(); } + + } + +} 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 9a19d41ce..34ca6d695 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 @@ -107,11 +107,11 @@ public class FullDataFileHandler implements IFullDataSourceProvider MetaFileScanUtil.IAddUnloadedFileFunc addUnloadedFileFunc = (pos, file) -> { this.unloadedFileBySectionPos.put(pos, file); - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); }; MetaFileScanUtil.IAddLoadedMetaFileFunc addLoadedMetaFileFunc = (pos, loadedMetaFile) -> { - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); this.metaFileBySectionPos.put(pos, (FullDataMetaFile) loadedMetaFile); }; @@ -138,7 +138,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider @Override public CompletableFuture readAsync(DhSectionPos pos) { - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); if (metaFile == null) { @@ -192,7 +192,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider try { metaFile = FullDataMetaFile.createFromExistingFile(this, this.level, fileToLoad); - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); this.metaFileBySectionPos.put(pos, metaFile); return metaFile; } @@ -227,7 +227,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider return null; } - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); // This is a CAS with expected null value. FullDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); @@ -244,22 +244,24 @@ public class FullDataFileHandler implements IFullDataSourceProvider DhSectionPos effectivePos, DhSectionPos posAreaToGet, ArrayList preexistingFiles, ArrayList missingFilePositions) { - byte sectionDetail = posAreaToGet.sectionDetailLevel; + byte sectionDetail = posAreaToGet.getDetailLevel(); boolean allEmpty = true; + final DhSectionPos.DhMutableSectionPos subPos = new DhSectionPos.DhMutableSectionPos((byte)0, 0, 0); + // get all existing files for this position outerLoop: while (--sectionDetail >= this.minDetailLevel) { - DhLodPos minPos = posAreaToGet.getCorner().getCornerLodPos(sectionDetail); + DhLodPos minPos = posAreaToGet.getMinCornerLodPos().getCornerLodPos(sectionDetail); int count = posAreaToGet.getSectionBBoxPos().getWidthAtDetail(sectionDetail); for (int xOffset = 0; xOffset < count; xOffset++) { for (int zOffset = 0; zOffset < count; zOffset++) { - DhSectionPos subPos = new DhSectionPos(sectionDetail, xOffset + minPos.x, zOffset + minPos.z); - LodUtil.assertTrue(posAreaToGet.overlaps(effectivePos) && subPos.overlaps(posAreaToGet)); + subPos.mutate(sectionDetail, xOffset + minPos.x, zOffset + minPos.z); + LodUtil.assertTrue(posAreaToGet.overlapsExactly(effectivePos) && subPos.overlapsExactly(posAreaToGet)); //TODO: The following check is temporary as we only sample corner points, which means // on a very different level, we may not need the entire section at all. @@ -311,7 +313,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider // we have reached a populated leaf node in the quad tree preexistingFiles.add(metaFile); } - else if (childPos.sectionDetailLevel == this.minDetailLevel) + else if (childPos.getDetailLevel() == this.minDetailLevel) { // we have reached an empty leaf node in the quad tree missingFilePositions.add(childPos); @@ -337,11 +339,11 @@ public class FullDataFileHandler implements IFullDataSourceProvider @Override public void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { - DhLodPos chunkPos = chunkDataView.getLodPos(); - LodUtil.assertTrue(chunkPos.overlapsExactly(sectionPos.getSectionBBoxPos()), "Chunk " + chunkPos + " does not overlap section " + sectionPos); + DhSectionPos chunkSectionPos = chunkDataView.getSectionPos(); + LodUtil.assertTrue(chunkSectionPos.overlapsExactly(sectionPos), "Chunk " + chunkSectionPos + " does not overlap section " + sectionPos); - chunkPos = chunkPos.convertToDetailLevel((byte) this.minDetailLevel); - this.writeChunkDataToMetaFile(new DhSectionPos(chunkPos.detailLevel, chunkPos.x, chunkPos.z), chunkDataView); + chunkSectionPos = chunkSectionPos.convertNewToDetailLevel((byte) this.minDetailLevel); + this.writeChunkDataToMetaFile(chunkSectionPos, chunkDataView); } private void writeChunkDataToMetaFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData) { @@ -352,7 +354,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider metaFile.addToWriteQueue(chunkData); } - if (sectionPos.sectionDetailLevel <= this.topDetailLevelRef.get()) + if (sectionPos.getDetailLevel() <= this.topDetailLevelRef.get()) { // recursively attempt to get the meta file for this position this.writeChunkDataToMetaFile(sectionPos.getParentPos(), chunkData); @@ -387,7 +389,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider protected IIncompleteFullDataSource makeEmptyDataSource(DhSectionPos pos) { - return pos.sectionDetailLevel <= HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL ? + return pos.getDetailLevel() <= HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL ? HighDetailIncompleteFullDataSource.createEmpty(pos) : LowDetailIncompleteFullDataSource.createEmpty(pos); } 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 43fa3825a..858f084e5 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 @@ -33,6 +33,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFull import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.distanthorizons.core.file.DataSourceReferenceTracker; import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; import com.seibel.distanthorizons.core.file.metaData.BaseMetaData; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -84,7 +85,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I * When clearing, don't set to null, instead create a SoftReference containing null. * This makes null checks simpler. */ - private SoftReference cachedFullDataSourceRef = new SoftReference<>(null); + private DataSourceReferenceTracker.FullDataSourceSoftRef cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this,null); private final AtomicReference> dataSourceLoadFutureRef = new AtomicReference<>(null); // === Concurrent Write tracking === @@ -343,7 +344,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { checkAndLogPhantomDataSourceLifeCycles(); - DhLodPos chunkLodPos = new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkAccessor.pos.x, chunkAccessor.pos.z); + DhLodPos chunkLodPos = new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkAccessor.chunkPos.x, chunkAccessor.chunkPos.z); LodUtil.assertTrue(this.pos.getSectionBBoxPos().overlapsExactly(chunkLodPos), "Chunk pos " + chunkLodPos + " doesn't exactly overlap with section " + this.pos); //LOGGER.info("Write Chunk {} to file {}", chunkPos, pos); @@ -401,7 +402,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I public static void checkAndLogPhantomDataSourceLifeCycles() { DataObjTracker phantomRef = (DataObjTracker) LIFE_CYCLE_DEBUG_QUEUE.poll(); - // wait for the tracker to be garbage collected(?) + // wait for the tracker to be garbage collected while (phantomRef != null) { if (LOG_DATA_SOURCE_LIVES) @@ -430,7 +431,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I @Override public void debugRender(DebugRenderer debugRenderer) { - if (this.pos.sectionDetailLevel > DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + if (this.pos.getDetailLevel() > DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { return; } @@ -554,7 +555,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I new DataObjSoftTracker(this, fullDataSource); } - if (this.pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + if (this.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { DebugRenderer.makeParticle(new DebugRenderer.BoxParticle( new DebugRenderer.Box(this.pos, 64f, 72f, 0.03f, Color.green.darker()), @@ -563,7 +564,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // save the updated data source - this.cachedFullDataSourceRef = new SoftReference<>(fullDataSource); + this.cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this, fullDataSource); // the task is complete completionFuture.complete(fullDataSource); @@ -685,7 +686,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I if (LOG_DATA_SOURCE_LIVES) { - LOGGER.info("Phantom created on {}! count: {}", data.getSectionPos(), LIFE_CYCLE_DEBUG_SET.size()); + //LOGGER.info("Phantom created on "+data.getSectionPos()+"! count: "+LIFE_CYCLE_DEBUG_SET.size()); } LIFE_CYCLE_DEBUG_SET.add(this); 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 242e36217..e15369033 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 @@ -355,7 +355,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler return (chunkSizedFullDataSource) -> { - if (chunkSizedFullDataSource.getLodPos().overlapsExactly(this.loadedTargetFullDataSource.getSectionPos().getSectionBBoxPos())) + if (chunkSizedFullDataSource.getSectionPos().overlapsExactly(this.loadedTargetFullDataSource.getSectionPos())) { ((DhLevel) level).saveWrites(chunkSizedFullDataSource); //GeneratedFullDataFileHandler.this.write(this.loadedTargetFullDataSource.getSectionPos(), chunkSizedFullDataSource); 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 168bae813..846e1b71c 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 @@ -25,7 +25,6 @@ import java.nio.channels.Channels; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.*; -import java.util.concurrent.locks.ReentrantLock; import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; @@ -231,11 +230,11 @@ public abstract class AbstractMetaDataContainerFile fileChannel.position(0); ByteBuffer buffer = ByteBuffer.allocate(METADATA_SIZE_IN_BYTES); buffer.putInt(METADATA_IDENTITY_BYTES); - buffer.putInt(this.pos.sectionX); + buffer.putInt(this.pos.getX()); buffer.putInt(Integer.MIN_VALUE); // Unused - y pos - buffer.putInt(this.pos.sectionZ); + buffer.putInt(this.pos.getZ()); buffer.putInt(this.baseMetaData.checksum); - buffer.put(this.pos.sectionDetailLevel); + buffer.put(this.pos.getDetailLevel()); 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 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/RenderDataMetaFile.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java rename to core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java index d5e739570..60991802a 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/RenderDataMetaFile.java @@ -23,14 +23,13 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer; +import com.seibel.distanthorizons.core.file.DataSourceReferenceTracker; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; import com.seibel.distanthorizons.core.file.metaData.BaseMetaData; -import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderLoader; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; @@ -47,13 +46,12 @@ import java.awt.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.lang.ref.SoftReference; import java.util.Random; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; /** Represents a File that contains a {@link ColumnRenderSource}. */ -public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements IDebugRenderable +public class RenderDataMetaFile extends AbstractMetaDataContainerFile implements IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -68,7 +66,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements * When clearing, don't set to null, instead create a SoftReference containing null. * This makes null checks simpler. */ - private SoftReference cachedRenderDataSource = new SoftReference<>(null); + private DataSourceReferenceTracker.RenderDataSourceSoftRef cachedRenderDataSource = new DataSourceReferenceTracker.RenderDataSourceSoftRef(this, null); private final AtomicReference> renderSourceLoadFutureRef = new AtomicReference<>(null); private final IDhClientLevel clientLevel; @@ -82,10 +80,10 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements //=============// /** - * Can be used instead of {@link RenderMetaDataFile#createFromExistingFile} or {@link RenderMetaDataFile#createNewFileForPos}, + * Can be used instead of {@link RenderDataMetaFile#createFromExistingFile} or {@link RenderDataMetaFile#createNewFileForPos}, * if we are uncertain whether a file exists or not. */ - public static RenderMetaDataFile createFromExistingOrNewFile(IDhClientLevel clientLevel, IFullDataSourceProvider fullDataSourceProvider, DhSectionPos pos, File file) throws IOException + public static RenderDataMetaFile createFromExistingOrNewFile(IDhClientLevel clientLevel, IFullDataSourceProvider fullDataSourceProvider, DhSectionPos pos, File file) throws IOException { if (file.exists()) { @@ -102,8 +100,8 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements * NOTE: should only be used if there is NOT an existing file. * @throws IOException if a file already exists for this position */ - public static RenderMetaDataFile createNewFileForPos(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, DhSectionPos pos, File file) throws IOException { return new RenderMetaDataFile(fullDataSourceProvider, clientLevel, pos, file); } - private RenderMetaDataFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, DhSectionPos pos, File file) throws IOException + public static RenderDataMetaFile createNewFileForPos(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, DhSectionPos pos, File file) throws IOException { return new RenderDataMetaFile(fullDataSourceProvider, clientLevel, pos, file); } + private RenderDataMetaFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, DhSectionPos pos, File file) throws IOException { super(file, pos); this.fullDataSourceProvider = fullDataSourceProvider; @@ -118,8 +116,8 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements * NOTE: should only be used if there IS an existing file. * @throws IOException if no file exists for this position */ - public static RenderMetaDataFile createFromExistingFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, File file) throws IOException { return new RenderMetaDataFile(fullDataSourceProvider, clientLevel, file); } - private RenderMetaDataFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, File file) throws IOException + public static RenderDataMetaFile createFromExistingFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, File file) throws IOException { return new RenderDataMetaFile(fullDataSourceProvider, clientLevel, file); } + private RenderDataMetaFile(IFullDataSourceProvider fullDataSourceProvider, IDhClientLevel clientLevel, File file) throws IOException { super(file); this.fullDataSourceProvider = fullDataSourceProvider; @@ -137,8 +135,8 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements public void updateChunkIfSourceExistsAsync(ChunkSizedFullDataAccessor chunkDataView) { - DhLodPos chunkPos = chunkDataView.getLodPos(); - LodUtil.assertTrue(this.pos.getSectionBBoxPos().overlapsExactly(chunkPos), "Chunk pos " + chunkPos + " doesn't overlap with section " + this.pos); + DhSectionPos chunkSectionPos = chunkDataView.getSectionPos(); + LodUtil.assertTrue(this.pos.overlapsExactly(chunkSectionPos), "Chunk pos " + chunkSectionPos + " doesn't overlap with section " + this.pos); // update the render source if one exists CompletableFuture renderSourceLoadFuture = this.getCachedDataSourceAsync(false); @@ -157,7 +155,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements Color debugColor = dataUpdated ? Color.blue : Color.red; DebugRenderer.makeParticle( new DebugRenderer.BoxParticle( - new DebugRenderer.Box(chunkDataView.getLodPos(), 32f, 64f + offset, 0.07f, debugColor), + new DebugRenderer.Box(chunkDataView.getSectionPos(), 32f, 64f + offset, 0.07f, debugColor), 2.0, 16f ) ); @@ -199,7 +197,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements // create an empty render source - byte dataDetailLevel = (byte) (this.pos.sectionDetailLevel - ColumnRenderSource.SECTION_SIZE_OFFSET); + byte dataDetailLevel = (byte) (this.pos.getDetailLevel() - ColumnRenderSource.SECTION_SIZE_OFFSET); int verticalSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(dataDetailLevel); ColumnRenderSource newColumnRenderSource = new ColumnRenderSource(this.pos, verticalSize, this.clientLevel.getMinY()); @@ -210,7 +208,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements this.updateRenderCacheAsync(newColumnRenderSource).whenComplete((voidObj, ex) -> { - this.cachedRenderDataSource = new SoftReference<>(newColumnRenderSource); + this.cachedRenderDataSource = new DataSourceReferenceTracker.RenderDataSourceSoftRef(this, newColumnRenderSource); this.renderSourceLoadFutureRef.set(null); getSourceFuture.complete(newColumnRenderSource); @@ -258,7 +256,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements this.renderSourceLoadFutureRef.set(null); - this.cachedRenderDataSource = new SoftReference<>(renderSource); + this.cachedRenderDataSource = new DataSourceReferenceTracker.RenderDataSourceSoftRef(this, renderSource); getSourceFuture.complete(renderSource); }); } @@ -465,7 +463,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements // helper methods // //================// - /** @return returns null if {@link RenderMetaDataFile#renderSourceLoadFutureRef} is empty and no cached {@link ColumnRenderSource} exists. */ + /** @return returns null if {@link RenderDataMetaFile#renderSourceLoadFutureRef} is empty and no cached {@link ColumnRenderSource} exists. */ @Nullable private CompletableFuture getCachedDataSourceAsync(boolean updateRenderSourceCache) { 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 40cb1eae8..9dc6a8bdc 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 @@ -23,7 +23,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; @@ -50,8 +49,8 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider private final F3Screen.NestedMessage threadPoolMsg; private final ConcurrentHashMap unloadedFileBySectionPos = new ConcurrentHashMap<>(); - /** contains the loaded {@link RenderMetaDataFile}'s */ - private final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); + /** contains the loaded {@link RenderDataMetaFile}'s */ + private final ConcurrentHashMap metaFileBySectionPos = new ConcurrentHashMap<>(); private final IDhClientLevel clientLevel; private final File saveDir; @@ -93,21 +92,21 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider @Override public void addScannedFiles(Collection detectedFiles) { - MetaFileScanUtil.ICreateMetadataFunc createMetadataFunc = (file) -> RenderMetaDataFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, file); + MetaFileScanUtil.ICreateMetadataFunc createMetadataFunc = (file) -> RenderDataMetaFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, file); MetaFileScanUtil.IAddUnloadedFileFunc addUnloadedFileFunc = (pos, file) -> { this.unloadedFileBySectionPos.put(pos, file); - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); }; MetaFileScanUtil.IAddLoadedMetaFileFunc addLoadedMetaFileFunc = (pos, loadedMetaFile) -> { - this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.sectionDetailLevel)); - this.metaFileBySectionPos.put(pos, (RenderMetaDataFile) loadedMetaFile); + this.topDetailLevelRef.updateAndGet(oldDetailLevel -> Math.max(oldDetailLevel, pos.getDetailLevel())); + this.metaFileBySectionPos.put(pos, (RenderDataMetaFile) loadedMetaFile); }; - MetaFileScanUtil.addScannedFiles(detectedFiles, USE_LAZY_LOADING, RenderMetaDataFile.FILE_SUFFIX, + MetaFileScanUtil.addScannedFiles(detectedFiles, USE_LAZY_LOADING, RenderDataMetaFile.FILE_SUFFIX, createMetadataFunc, addUnloadedFileFunc, addLoadedMetaFileFunc); } @@ -130,7 +129,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider - RenderMetaDataFile metaFile = this.getLoadOrMakeFile(pos); + RenderDataMetaFile metaFile = this.getLoadOrMakeFile(pos); if (metaFile == null) { return CompletableFuture.completedFuture(ColumnRenderSource.createEmptyRenderSource(pos)); @@ -154,9 +153,9 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider return getDataSourceFuture; } /** @return null if there was an issue */ - private RenderMetaDataFile getLoadOrMakeFile(DhSectionPos pos) + private RenderDataMetaFile getLoadOrMakeFile(DhSectionPos pos) { - RenderMetaDataFile metaFile = this.metaFileBySectionPos.get(pos); + RenderDataMetaFile metaFile = this.metaFileBySectionPos.get(pos); if (metaFile != null) { // return the loaded file @@ -194,8 +193,8 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider // attempt to load the file try { - metaFile = RenderMetaDataFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, fileToLoad); - this.topDetailLevelRef.updateAndGet(currentTopDetailLevel -> Math.max(currentTopDetailLevel, pos.sectionDetailLevel)); + metaFile = RenderDataMetaFile.createFromExistingFile(this.fullDataSourceProvider, this.clientLevel, fileToLoad); + this.topDetailLevelRef.updateAndGet(currentTopDetailLevel -> Math.max(currentTopDetailLevel, pos.getDetailLevel())); this.metaFileBySectionPos.put(pos, metaFile); return metaFile; } @@ -219,12 +218,12 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { // 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.clientLevel, this.fullDataSourceProvider, pos, this.computeRenderFilePath(pos)); + metaFile = RenderDataMetaFile.createFromExistingOrNewFile(this.clientLevel, this.fullDataSourceProvider, pos, this.computeRenderFilePath(pos)); - this.topDetailLevelRef.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.sectionDetailLevel)); + this.topDetailLevelRef.updateAndGet(newDetailLevel -> Math.max(newDetailLevel, pos.getDetailLevel())); // 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); + RenderDataMetaFile metaFileCas = this.metaFileBySectionPos.putIfAbsent(pos, metaFile); return (metaFileCas == null) ? metaFile : metaFileCas; } catch (IOException e) @@ -253,16 +252,18 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider } private void writeChunkDataToFileRecursively(ChunkSizedFullDataAccessor chunk, byte sectionDetailLevel) { - DhLodPos boundingPos = chunk.getLodPos(); - DhLodPos minSectionPos = boundingPos.convertToDetailLevel(sectionDetailLevel); + DhSectionPos boundingPos = chunk.getSectionPos(); + DhSectionPos minSectionPos = boundingPos.convertNewToDetailLevel(sectionDetailLevel); - int width = (sectionDetailLevel > boundingPos.detailLevel) ? 1 : boundingPos.getWidthAtDetail(sectionDetailLevel); + DhSectionPos.DhMutableSectionPos fileSectionPos = new DhSectionPos.DhMutableSectionPos((byte)0, 0, 0); + + int width = (sectionDetailLevel > boundingPos.getDetailLevel()) ? 1 : boundingPos.getWidthCountForLowerDetailedSection(sectionDetailLevel); for (int xOffset = 0; xOffset < width; xOffset++) { for (int zOffset = 0; zOffset < width; zOffset++) { - 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. + fileSectionPos.mutate(sectionDetailLevel, minSectionPos.getX() + xOffset, minSectionPos.getZ() + zOffset); + RenderDataMetaFile metaFile = this.metaFileBySectionPos.get(fileSectionPos); // bypass the getLoadOrMakeFile() since we only want cached files. if (metaFile != null) { metaFile.updateChunkIfSourceExistsAsync(chunk); @@ -284,7 +285,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider LOGGER.info("Shutting down " + RenderSourceFileHandler.class.getSimpleName() + "..."); ArrayList> futures = new ArrayList<>(); - for (RenderMetaDataFile metaFile : this.metaFileBySectionPos.values()) + for (RenderDataMetaFile metaFile : this.metaFileBySectionPos.values()) { futures.add(metaFile.flushAndSaveAsync()); } @@ -378,7 +379,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider // helper methods // //================// - public File computeRenderFilePath(DhSectionPos pos) { return new File(this.saveDir, pos.serialize() + RenderMetaDataFile.FILE_SUFFIX); } + public File computeRenderFilePath(DhSectionPos pos) { return new File(this.saveDir, pos.serialize() + RenderDataMetaFile.FILE_SUFFIX); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index b32426a42..3cf6f863b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -28,7 +28,6 @@ import com.seibel.distanthorizons.core.generation.tasks.*; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.transformers.LodDataBuilder; @@ -45,7 +44,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import org.apache.logging.log4j.Logger; import java.awt.*; -import java.io.Closeable; import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; @@ -159,7 +157,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender } // Assert that the data at least can fill in 1 single ChunkSizedFullDataAccessor - LodUtil.assertTrue(pos.sectionDetailLevel > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); + LodUtil.assertTrue(pos.getDetailLevel() > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); //if (this.waitingTaskQuadTree.isSectionPosInBounds(requestPos)) @@ -393,14 +391,14 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // split up the task and add each one to the tree LinkedList> childFutures = new LinkedList<>(); - DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.sectionDetailLevel, closestTask.pos.sectionX, closestTask.pos.sectionZ); + DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.getDetailLevel(), closestTask.pos.getX(), closestTask.pos.getZ()); WorldGenTask finalClosestTask = closestTask; sectionPos.forEachChild((childDhSectionPos) -> { CompletableFuture newFuture = new CompletableFuture<>(); childFutures.add(newFuture); - WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.sectionDetailLevel, finalClosestTask.taskTracker, newFuture); + WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.getDetailLevel(), finalClosestTask.taskTracker, newFuture); waitingTasks.put(newGenTask.pos, newGenTask); //this.waitingTaskQuadTree.setValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask); @@ -421,7 +419,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender { byte taskDetailLevel = inProgressTaskGroup.group.dataDetail; DhSectionPos taskPos = inProgressTaskGroup.group.pos; - byte granularity = (byte) (taskPos.sectionDetailLevel - taskDetailLevel); + byte granularity = (byte) (taskPos.getDetailLevel() - taskDetailLevel); LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity); LodUtil.assertTrue(taskDetailLevel >= this.smallestDataDetail && taskDetailLevel <= this.largestDataDetail); @@ -436,7 +434,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender //StackTraceElement[] stackTrace = this.alreadyGeneratedPosHashSet.get(inProgressTaskGroup.group.pos); // sending a success result is necessary to make sure the render sections are reloaded correctly - inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.sectionX, taskPos.sectionZ)))); + inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); return; } this.alreadyGeneratedPosHashSet.put(inProgressTaskGroup.group.pos, Thread.currentThread().getStackTrace()); @@ -470,7 +468,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender else { //LOGGER.info("Section generation at "+pos+" completed"); - inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.sectionX, taskPos.sectionZ)))); + inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); } boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, inProgressTaskGroup); LodUtil.assertTrue(worked); @@ -686,7 +684,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , DhSectionPos taskPos) { - byte granularity = (byte) (taskPos.sectionDetailLevel - worldGenTaskGroupDetailLevel); + byte granularity = (byte) (taskPos.getDetailLevel() - worldGenTaskGroupDetailLevel); return (granularity >= this.minGranularity && granularity <= this.maxGranularity); } 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 ac3ec177c..a75d24755 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 @@ -175,16 +175,16 @@ public class ClientLevelModule implements Closeable //===============// public void writeChunkDataToFile(ChunkSizedFullDataAccessor data) { - DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); + DhSectionPos pos = data.getSectionPos().convertNewToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState != null) { - ClientRenderState.renderSourceFileHandler.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + ClientRenderState.renderSourceFileHandler.writeChunkDataToFile(pos, data); } else { - this.parentClientLevel.getFileHandler().writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + this.parentClientLevel.getFileHandler().writeChunkDataToFile(pos, data); } } 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 202502855..36379395f 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 @@ -279,8 +279,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel @Override public void saveWrites(ChunkSizedFullDataAccessor data) { - DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); - getFileHandler().writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + DhSectionPos pos = data.getSectionPos(); + pos = pos.convertNewToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); + this.getFileHandler().writeChunkDataToFile(pos, data); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java index 7ea8b68e6..c6f8c1906 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java @@ -26,6 +26,13 @@ public class DhBlockPos2D public static final DhBlockPos2D ZERO = new DhBlockPos2D(0, 0); public final int x; public final int z; + + + + //==============// + // constructors // + //==============// + public DhBlockPos2D(int x, int z) { this.x = x; @@ -38,44 +45,42 @@ public class DhBlockPos2D this.z = blockPos.z; } - public DhBlockPos2D add(DhBlockPos2D other) - { - return new DhBlockPos2D(x + other.x, z + other.z); - } + public static DhBlockPos2D fromPos2D(Pos2D pos) { return new DhBlockPos2D(pos.x, pos.y); } - public DhBlockPos2D add(int offsetX, int offsetZ) - { - return new DhBlockPos2D(x + offsetX, z + offsetZ); - } - public DhBlockPos2D subtract(DhBlockPos2D other) - { - return new DhBlockPos2D(x - other.x, z - other.z); - } - public double dist(DhBlockPos2D other) - { - return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); - } - public long distSquared(DhBlockPos2D other) - { - return MathUtil.pow2((long) x - other.x) + MathUtil.pow2((long) z - other.z); - } - public Pos2D toPos2D() - { - return new Pos2D(x, z); - } + //==========// + // mutators // + //==========// - public static DhBlockPos2D fromPos2D(Pos2D pos) - { - return new DhBlockPos2D(pos.x, pos.y); - } + public DhBlockPos2D add(DhBlockPos2D other) { return new DhBlockPos2D(this.x + other.x, this.z + other.z); } + + public DhBlockPos2D add(int offsetX, int offsetZ) { return new DhBlockPos2D(this.x + offsetX, this.z + offsetZ); } + + public DhBlockPos2D subtract(DhBlockPos2D other) { return new DhBlockPos2D(this.x - other.x, this.z - other.z); } + + public Pos2D toPos2D() { return new Pos2D(this.x, this.z); } + + + + //==============// + // calculations // + //==============// + + public double dist(DhBlockPos2D other) { return this.dist(other.x, other.z); } + public double dist(int x, int z) { return Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.z - z, 2)); } + + public long distSquared(DhBlockPos2D other) { return this.distSquared(other.x, other.z); } + public long distSquared(int x, int z) { return MathUtil.pow2((long) this.x - x) + MathUtil.pow2((long) this.z - z); } + + + + //===========// + // overrides // + //===========// @Override - public String toString() - { - return "(" + x + ", " + z + ")"; - } + public String toString() { return "(" + this.x + ", " + this.z + ")"; } @Override public boolean equals(Object obj) @@ -83,15 +88,13 @@ public class DhBlockPos2D if (obj instanceof DhBlockPos2D) { DhBlockPos2D other = (DhBlockPos2D) obj; - return x == other.x && z == other.z; + return this.x == other.x && this.z == other.z; } + return false; } @Override - public int hashCode() - { - return Integer.hashCode(x) ^ Integer.hashCode(z); - } + public int hashCode() { return Integer.hashCode(this.x) ^ Integer.hashCode(this.z); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java index f3aa7df76..21273d79a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java @@ -47,8 +47,7 @@ public class DhLodPos implements Comparable this.x = x; this.z = z; } - public DhLodPos(DhSectionPos sectionPos) { this(sectionPos.sectionDetailLevel, sectionPos.sectionX, sectionPos.sectionZ); } - + public DhLodPos(DhSectionPos sectionPos) { this(sectionPos.getDetailLevel(), sectionPos.getX(), sectionPos.getZ()); } @@ -193,11 +192,14 @@ public class DhLodPos implements Comparable public boolean overlapsExactly(DhLodPos other) { if (this.equals(other)) + { return true; - if (this.detailLevel == other.detailLevel) + } + else if (this.detailLevel == other.detailLevel) + { return false; - - if (this.detailLevel > other.detailLevel) + } + else if (this.detailLevel > other.detailLevel) { return this.equals(other.convertToDetailLevel(this.detailLevel)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index fc744673c..b453ec777 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.util.LodUtil; import io.netty.buffer.ByteBuf; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -44,7 +43,6 @@ import java.util.function.Consumer; * With those thoughts in mind we decided on a smallest section size of 32 data points square (IE 2x2 chunks). * * @author Leetom - * @version 2022-11-6 */ public class DhSectionPos implements INetworkObject { @@ -59,116 +57,170 @@ public class DhSectionPos implements INetworkObject public final static byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL; - public byte sectionDetailLevel; + protected byte detailLevel; /** in a sectionDetailLevel grid */ - public int sectionX; + protected int x; /** in a sectionDetailLevel grid */ - public int sectionZ; + protected int z; public static DhSectionPos zero() { return new DhSectionPos((byte) 0, 0, 0); }; - + //==============// // constructors // //==============// - - public DhSectionPos(byte sectionDetailLevel, int sectionX, int sectionZ) + + public DhSectionPos(byte detailLevel, int x, int z) { - this.sectionDetailLevel = sectionDetailLevel; - this.sectionX = sectionX; - this.sectionZ = sectionZ; + this.detailLevel = detailLevel; + this.x = x; + this.z = z; } - public DhSectionPos(DhBlockPos blockPos) { this(new DhBlockPos2D(blockPos)); } + public DhSectionPos(DhBlockPos blockPos) + { + this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z); + this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + } public DhSectionPos(DhBlockPos2D blockPos) { - DhLodPos lodPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z); - lodPos = lodPos.convertToDetailLevel(SECTION_BLOCK_DETAIL_LEVEL); - - this.sectionDetailLevel = SECTION_BLOCK_DETAIL_LEVEL; - this.sectionX = lodPos.x; - this.sectionZ = lodPos.z; + this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z); + this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); } public DhSectionPos(DhChunkPos chunkPos) { - DhLodPos lodPos = new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z); - lodPos = lodPos.convertToDetailLevel(SECTION_CHUNK_DETAIL_LEVEL); - - this.sectionDetailLevel = SECTION_CHUNK_DETAIL_LEVEL; - this.sectionX = lodPos.x; - this.sectionZ = lodPos.z; + this(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z); + this.convertSelfToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); } public DhSectionPos(byte detailLevel, DhLodPos dhLodPos) { - this.sectionDetailLevel = detailLevel; - this.sectionX = dhLodPos.x; - this.sectionZ = dhLodPos.z; + this.detailLevel = detailLevel; + this.x = dhLodPos.x; + this.z = dhLodPos.z; } - /** Returns the center for the highest detail level (0) */ - public DhLodPos getCenter() { return this.getCenter((byte) 0); } // TODO why does this use detail level 0 instead of this object's detail level? - public DhLodPos getCenter(byte returnDetailLevel) - { - LodUtil.assertTrue(returnDetailLevel <= this.sectionDetailLevel, "returnDetailLevel must be less than sectionDetail"); - - if (returnDetailLevel == this.sectionDetailLevel) - { - return new DhLodPos(this.sectionDetailLevel, this.sectionX, this.sectionZ); - } - - byte detailLevelOffset = (byte) (this.sectionDetailLevel - returnDetailLevel); - - // we can't get the center of the position at block level, only attempt to get the position offset for detail levels above 0 // TODO should this also apply to detail level 1 or is it fine? - int positionOffset = 0; - if (this.sectionDetailLevel != 1 || returnDetailLevel != 0) - { - positionOffset = BitShiftUtil.powerOfTwo(detailLevelOffset - 1); - } - - return new DhLodPos(returnDetailLevel, - (this.sectionX * BitShiftUtil.powerOfTwo(detailLevelOffset)) + positionOffset, - (this.sectionZ * BitShiftUtil.powerOfTwo(detailLevelOffset)) + positionOffset); - } - /** @return the corner with the smallest X and Z coordinate */ - public DhLodPos getCorner() { return this.getCorner((byte) (this.sectionDetailLevel - 1)); } - /** @return the corner with the smallest X and Z coordinate */ - public DhLodPos getCorner(byte returnDetailLevel) - { - LodUtil.assertTrue(returnDetailLevel <= this.sectionDetailLevel, "returnDetailLevel must be less than sectionDetail"); - byte offset = (byte) (this.sectionDetailLevel - returnDetailLevel); - return new DhLodPos(returnDetailLevel, - this.sectionX * BitShiftUtil.powerOfTwo(offset), - this.sectionZ * BitShiftUtil.powerOfTwo(offset)); - } - - public DhLodUnit getWidth() { return this.getWidth(this.sectionDetailLevel); } - public DhLodUnit getWidth(byte returnDetailLevel) - { - LodUtil.assertTrue(returnDetailLevel <= this.sectionDetailLevel, "returnDetailLevel must be less than sectionDetail"); - byte offset = (byte) (this.sectionDetailLevel - returnDetailLevel); - return new DhLodUnit(this.sectionDetailLevel, BitShiftUtil.powerOfTwo(offset)); - } + //============// + // converters // + //============// /** - * uses the absolute detail level aka detail levels like {@link LodUtil#CHUNK_DETAIL_LEVEL} instead of the dhSectionPos detailLevels + * uses the absolute detail level aka detail levels like {@link LodUtil#CHUNK_DETAIL_LEVEL} instead of the dhSectionPos detailLevels. * * @return the new position closest to negative infinity with the new detail level */ - public DhSectionPos convertToDetailLevel(byte newSectionDetailLevel) + public DhSectionPos convertNewToDetailLevel(byte newSectionDetailLevel) { - DhLodPos lodPos = new DhLodPos(this.sectionDetailLevel, this.sectionX, this.sectionZ); - lodPos = lodPos.convertToDetailLevel(newSectionDetailLevel); + DhSectionPos newPos = new DhSectionPos(this.detailLevel, this.x, this.z); + newPos.convertSelfToDetailLevel(newSectionDetailLevel); - DhSectionPos newPos = new DhSectionPos(newSectionDetailLevel, lodPos); return newPos; } + /** uses the absolute detail level aka detail levels like {@link LodUtil#CHUNK_DETAIL_LEVEL} instead of the dhSectionPos detailLevels. */ + protected void convertSelfToDetailLevel(byte newDetailLevel) + { + // logic originally taken from DhLodPos + if (newDetailLevel >= this.detailLevel) + { + this.x = Math.floorDiv(this.x, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel)); + this.z = Math.floorDiv(this.z, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel)); + } + else + { + this.x = this.x * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel); + this.z = this.z * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel); + } + + this.detailLevel = newDetailLevel; + } + + + + //==================// + // property getters // + //==================// + + public byte getDetailLevel() { return this.detailLevel; } + + public int getX() { return this.x; } + public int getZ() { return this.z; } + + + + //=========// + // getters // + //=========// + + /** @return the corner with the smallest X and Z coordinate */ + public DhLodPos getMinCornerLodPos() { return this.getMinCornerLodPos((byte) (this.detailLevel - 1)); } + /** @return the corner with the smallest X and Z coordinate */ + public DhLodPos getMinCornerLodPos(byte returnDetailLevel) + { + LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail"); + + byte offset = (byte) (this.detailLevel - returnDetailLevel); + return new DhLodPos(returnDetailLevel, + this.x * BitShiftUtil.powerOfTwo(offset), + this.z * BitShiftUtil.powerOfTwo(offset)); + } + + /** + * A detail level of X lower than this section's detail level will return:
+ * 0 -> 1
+ * 1 -> 2
+ * 2 -> 4
+ * 3 -> 8
+ * etc. + * + * @return how many {@link DhSectionPos}'s at the given detail level it would take to span the width of this section. + */ + public int getWidthCountForLowerDetailedSection(byte returnDetailLevel) + { + LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail"); + byte offset = (byte) (this.detailLevel - returnDetailLevel); + return BitShiftUtil.powerOfTwo(offset); + } + + /** @return how wide this section is in blocks */ + public int getBlockWidth() { return BitShiftUtil.powerOfTwo(this.detailLevel); } + + + public DhBlockPos2D getCenterBlockPos() { return new DhBlockPos2D(this.getCenterBlockPosX(), this.getCenterBlockPosZ()); } + + public int getCenterBlockPosX() { return this.getCenterBlockPos(true); } + public int getCenterBlockPosZ() { return this.getCenterBlockPos(false); } + private int getCenterBlockPos(boolean returnX) + { + int centerBlockPos = returnX ? this.x : this.z; + + if (this.detailLevel == 0) + { + // already at block detail level, no conversion necessary + return centerBlockPos; + } + + // we can't get the center of the position at block level, only attempt to get the position offset for detail levels above 0 + int positionOffset = 0; + if (this.detailLevel != 1) + { + positionOffset = BitShiftUtil.powerOfTwo(this.detailLevel - 1); + } + + return (centerBlockPos * BitShiftUtil.powerOfTwo(this.detailLevel)) + positionOffset; + } + + + + //==================// + // parent child pos // + //==================// + /** * Returns the DhLodPos 1 detail level lower

* @@ -183,16 +235,79 @@ public class DhSectionPos implements INetworkObject public DhSectionPos getChildByIndex(int child0to3) throws IllegalArgumentException, IllegalStateException { if (child0to3 < 0 || child0to3 > 3) + { throw new IllegalArgumentException("child0to3 must be between 0 and 3"); - if (this.sectionDetailLevel <= 0) + } + if (this.detailLevel <= 0) + { throw new IllegalStateException("section detail must be greater than 0"); + } - return new DhSectionPos((byte) (this.sectionDetailLevel - 1), - this.sectionX * 2 + (child0to3 & 1), - this.sectionZ * 2 + BitShiftUtil.half(child0to3 & 2)); + return new DhSectionPos((byte) (this.detailLevel - 1), + this.x * 2 + (child0to3 & 1), + this.z * 2 + BitShiftUtil.half(child0to3 & 2)); } /** Returns this position's child index in its parent */ - public int getChildIndexOfParent() { return (this.sectionX & 1) + BitShiftUtil.square(this.sectionZ & 1); } + public int getChildIndexOfParent() { return (this.x & 1) + BitShiftUtil.square(this.z & 1); } + + public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.detailLevel + 1), BitShiftUtil.half(this.x), BitShiftUtil.half(this.z)); } + + + + + public DhSectionPos getAdjacentPos(EDhDirection dir) + { + return new DhSectionPos(this.detailLevel, + this.x + dir.getNormal().x, + this.z + dir.getNormal().z); + } + + public DhLodPos getSectionBBoxPos() { return new DhLodPos(this.detailLevel, this.x, this.z); } + + + + //=============// + // comparisons // + //=============// + + public boolean overlapsExactly(DhSectionPos other) + { + // original logic from DhLodPos + if (this.equals(other)) + { + return true; + } + else if (this.detailLevel == other.detailLevel) + { + return false; + } + else if (this.detailLevel > other.detailLevel) + { + return this.equals(other.convertNewToDetailLevel(this.detailLevel)); + } + else + { + return other.equals(this.convertNewToDetailLevel(other.detailLevel)); + } + } + + public boolean contains(DhSectionPos otherPos) + { + DhBlockPos2D thisMinBlockPos = this.getMinCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos(); + DhBlockPos2D otherCornerBlockPos = otherPos.getMinCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos(); + + int thisBlockWidth = this.getBlockWidth() - 1; // minus 1 to account for zero based positional indexing + DhBlockPos2D thisMaxBlockPos = new DhBlockPos2D(thisMinBlockPos.x + thisBlockWidth, thisMinBlockPos.z + thisBlockWidth); + + return thisMinBlockPos.x <= otherCornerBlockPos.x && otherCornerBlockPos.x <= thisMaxBlockPos.x && + thisMinBlockPos.z <= otherCornerBlockPos.z && otherCornerBlockPos.z <= thisMaxBlockPos.z; + } + + + + //===========// + // iterators // + //===========// /** Applies the given consumer to all 4 of this position's children. */ public void forEachChild(Consumer callback) @@ -206,46 +321,26 @@ public class DhSectionPos implements INetworkObject /** Applies the given consumer to all children of the position at the given section detail level. */ public void forEachChildAtLevel(byte sectionDetailLevel, Consumer callback) { - if (sectionDetailLevel == this.sectionDetailLevel) + if (sectionDetailLevel == this.detailLevel) { callback.accept(this); return; } + for (int i = 0; i < 4; i++) { this.getChildByIndex(i).forEachChildAtLevel(sectionDetailLevel, callback); } } - public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.sectionDetailLevel + 1), BitShiftUtil.half(this.sectionX), BitShiftUtil.half(this.sectionZ)); } - public DhSectionPos getAdjacentPos(EDhDirection dir) - { - return new DhSectionPos(this.sectionDetailLevel, - this.sectionX + dir.getNormal().x, - this.sectionZ + dir.getNormal().z); - } - public DhLodPos getSectionBBoxPos() { return new DhLodPos(this.sectionDetailLevel, this.sectionX, this.sectionZ); } - - /** NOTE: This does not consider yOffset! */ - public boolean overlaps(DhSectionPos other) { return this.getSectionBBoxPos().overlapsExactly(other.getSectionBBoxPos()); } - - /** NOTE: This does not consider yOffset! */ - public boolean contains(DhSectionPos otherPos) - { - DhBlockPos2D thisMinBlockPos = this.getCorner(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos(); - DhBlockPos2D otherCornerBlockPos = otherPos.getCorner(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos(); - - int thisBlockWidth = this.getWidth().toBlockWidth() - 1; // minus 1 to account for zero based positional indexing - DhBlockPos2D thisMaxBlockPos = new DhBlockPos2D(thisMinBlockPos.x + thisBlockWidth, thisMinBlockPos.z + thisBlockWidth); - - return thisMinBlockPos.x <= otherCornerBlockPos.x && otherCornerBlockPos.x <= thisMaxBlockPos.x && - thisMinBlockPos.z <= otherCornerBlockPos.z && otherCornerBlockPos.z <= thisMaxBlockPos.z; - } + //===============// + // serialization // + //===============// /** Serialize() is different from toString() as it must NEVER be changed, and should be in a short format */ - public String serialize() { return "[" + this.sectionDetailLevel + ',' + this.sectionX + ',' + this.sectionZ + ']'; } + public String serialize() { return "[" + this.detailLevel + ',' + this.x + ',' + this.z + ']'; } @Nullable public static DhSectionPos deserialize(String value) @@ -257,42 +352,112 @@ public class DhSectionPos implements INetworkObject } + + + //===========// + // overrides // + //===========// + @Override - public String toString() { return "{" + this.sectionDetailLevel + "*" + this.sectionX + "," + this.sectionZ + "}"; } + public String toString() { return "{" + this.detailLevel + "*" + this.x + "," + this.z + "}"; } @Override public boolean equals(Object obj) { if (this == obj) + { return true; - if (obj == null || this.getClass() != obj.getClass()) + } + if (obj == null || obj.getClass() != DhSectionPos.class) + { return false; + } DhSectionPos that = (DhSectionPos) obj; - return this.sectionDetailLevel == that.sectionDetailLevel && - this.sectionX == that.sectionX && - this.sectionZ == that.sectionZ; + return this.detailLevel == that.detailLevel && + this.x == that.x && + this.z == that.z; } @Override public int hashCode() { - return Integer.hashCode(this.sectionDetailLevel) ^ // XOR - Integer.hashCode(this.sectionX) ^ // XOR - Integer.hashCode(this.sectionZ); + return Integer.hashCode(this.detailLevel) ^ // XOR + Integer.hashCode(this.x) ^ // XOR + Integer.hashCode(this.z); } - + + + + //=============// + // sub classes // + //=============// + + /** + * Identical to {@link DhSectionPos} except it is mutable. + * See {@link DhSectionPos} for full documentation. + * + * @see DhSectionPos + */ + public static class DhMutableSectionPos extends DhSectionPos + { + + //==============// + // constructors // + //==============// + + public DhMutableSectionPos(byte sectionDetailLevel, int sectionX, int sectionZ) { super(sectionDetailLevel, sectionX, sectionZ); } + public DhMutableSectionPos(DhBlockPos blockPos) { super(blockPos); } + public DhMutableSectionPos(DhBlockPos2D blockPos) { super(blockPos); } + public DhMutableSectionPos(DhChunkPos chunkPos) { super(chunkPos); } + public DhMutableSectionPos(byte detailLevel, DhLodPos dhLodPos) { super(detailLevel, dhLodPos); } + + + + //============// + // converters // + //============// + + /** + * Overwrites this section pos with the given input.
+ * Can be useful to prevent duplicate allocations in high traffic loops but should + * be used sparingly as it could accidentally cause bugs due to unexpected modifications. + */ + public void mutate(byte sectionDetailLevel, int sectionX, int sectionZ) + { + this.detailLevel = sectionDetailLevel; + this.x = sectionX; + this.z = sectionZ; + } + + @Override + public void convertSelfToDetailLevel(byte newDetailLevel) { super.convertSelfToDetailLevel(newDetailLevel); } + + + + //==================// + // property getters // + //==================// + + public void setDetailLevel(byte sectionDetailLevel) { this.detailLevel = sectionDetailLevel; } + + public void setX(int sectionX) { this.x = sectionX; } + + public void setZ(int sectionZ) { this.z = sectionZ; } + + } + @Override public void encode(ByteBuf out) { - out.writeByte(this.sectionDetailLevel); - out.writeInt(this.sectionX); - out.writeInt(this.sectionZ); + out.writeByte(this.detailLevel); + out.writeInt(this.x); + out.writeInt(this.z); } @Override public void decode(ByteBuf in) { - this.sectionDetailLevel = in.readByte(); - this.sectionX = in.readInt(); - this.sectionZ = in.readInt(); + this.detailLevel = in.readByte(); + this.x = in.readInt(); + this.z = in.readInt(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 0a81cc761..53dee52a2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -143,7 +143,7 @@ public class LodQuadTree extends QuadTree implements AutoClose { // walk up the tree until we hit the root node // this is done so any high detail changes flow up to the lower detail render sections as well - while (pos.sectionDetailLevel <= this.treeMinDetailLevel) + while (pos.getDetailLevel() <= this.treeMinDetailLevel) { try { @@ -223,7 +223,7 @@ public class LodQuadTree extends QuadTree implements AutoClose expectedDetailLevel += DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL; - if (sectionPos.sectionDetailLevel > expectedDetailLevel) + if (sectionPos.getDetailLevel() > expectedDetailLevel) { // section detail level too high // boolean canThisPosRender = renderSection.isRenderingEnabled(); @@ -273,7 +273,7 @@ public class LodQuadTree extends QuadTree implements AutoClose } } // TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out - else if (sectionPos.sectionDetailLevel == expectedDetailLevel || sectionPos.sectionDetailLevel == expectedDetailLevel - 1) + else if (sectionPos.getDetailLevel() == expectedDetailLevel || sectionPos.getDetailLevel() == expectedDetailLevel - 1) { // this is the detail level we want to render // // prepare this section for rendering @@ -320,7 +320,7 @@ public class LodQuadTree extends QuadTree implements AutoClose * @param sectionPos section position * @return detail level of this section pos */ - public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenter().getCenterBlockPos())); } + public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenterBlockPosX(), sectionPos.getCenterBlockPosZ())); } private byte getDetailLevelFromDistance(double distance) { // special case, never drop the quality 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 18e06d240..eaa32502e 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 @@ -160,7 +160,7 @@ public class LodRenderSection implements IDebugRenderable public void reload(ILodRenderSourceProvider renderDataProvider) { // debug rendering - if (this.pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + if (this.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { DebugRenderer.makeParticle( new DebugRenderer.BoxParticle( @@ -324,7 +324,7 @@ public class LodRenderSection implements IDebugRenderable if (this.canBuildBuffer()) { // debug - if (this.pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + if (this.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { DebugRenderer.makeParticle( new DebugRenderer.BoxParticle( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index db50ae3fa..502537ce2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -118,13 +118,13 @@ public class RenderBufferHandler } } - Pos2D cPos = lodQuadTree.getCenterBlockPos().toPos2D(); + Pos2D cPos = this.lodQuadTree.getCenterBlockPos().toPos2D(); // Now that we have the axis directions, we can sort the render list Comparator farToNearComparator = (loadedBufferA, loadedBufferB) -> { - Pos2D aPos = loadedBufferA.pos.getCenter().getCenterBlockPos().toPos2D(); - Pos2D bPos = loadedBufferB.pos.getCenter().getCenterBlockPos().toPos2D(); + Pos2D aPos = loadedBufferA.pos.getCenterBlockPos().toPos2D(); + Pos2D bPos = loadedBufferB.pos.getCenterBlockPos().toPos2D(); if (true) { int aManhattanDistance = aPos.manhattanDist(cPos); @@ -161,7 +161,7 @@ public class RenderBufferHandler return abPosDifference; } - return loadedBufferA.pos.sectionDetailLevel - loadedBufferB.pos.sectionDetailLevel; // If all else fails, sort by detail + return loadedBufferA.pos.getDetailLevel() - loadedBufferB.pos.getDetailLevel(); // If all else fails, sort by detail }; // Build the sorted list diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index da4d5c7fe..2403a71a3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.render.glObject; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.seibel.distanthorizons.api.enums.config.EGLErrorHandlingMode; +import com.seibel.distanthorizons.api.enums.config.EGlProfileMode; import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -160,10 +161,27 @@ public class GLProxy // DO NOT comment out the following 2 lines: they are needed for mac and creating forward compatible contexts GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3); GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 2); - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE); + // TODO remove me + boolean useForwardCompatibility = Config.Client.Advanced.Debugging.glForwardCompatibilityMode.get(); + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, useForwardCompatibility ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE); + + // TODO remove me + EGlProfileMode profileMode = Config.Client.Advanced.Debugging.glProfileMode.get(); + if (profileMode == EGlProfileMode.CORE) + { + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); + } + else if (profileMode == EGlProfileMode.ANY) + { + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_ANY_PROFILE); + } + else + { + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_COMPAT_PROFILE); + } + // create the Lod Builder context lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext); if (lodBuilderGlContext == 0) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java index 3b17c65f5..726c92d0a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/MetaFileScanUtil.java @@ -25,7 +25,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.metaData.AbstractMetaDataContainerFile; import com.seibel.distanthorizons.core.file.renderfile.ILodRenderSourceProvider; -import com.seibel.distanthorizons.core.file.renderfile.RenderMetaDataFile; +import com.seibel.distanthorizons.core.file.renderfile.RenderDataMetaFile; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -81,7 +81,7 @@ public class MetaFileScanUtil try (Stream pathStream = Files.walk(saveStructure.getRenderCacheFolder(levelWrapper).toPath(), MAX_SCAN_DEPTH)) { List files = pathStream.filter( - path -> path.toFile().getName().endsWith(RenderMetaDataFile.FILE_SUFFIX) && path.toFile().isFile() + path -> path.toFile().getName().endsWith(RenderDataMetaFile.FILE_SUFFIX) && path.toFile().isFile() ).map(Path::toFile).collect(Collectors.toList()); LOGGER.info("Found " + files.size() + " render cache files for " + levelWrapper + " in " + saveStructure); renderSourceProvider.addScannedFiles(files); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java index e2b585c40..a8794be82 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java @@ -179,21 +179,21 @@ public class QuadNode if (!this.sectionPos.contains(inputSectionPos)) { - LOGGER.error((replaceValue ? "set " : "get ") + inputSectionPos + " center block: " + inputSectionPos.getCenter().getCornerBlockPos() + ", this pos: " + this.sectionPos + " this center block: " + this.sectionPos.getCenter().getCornerBlockPos()); - throw new IllegalArgumentException("Input section pos " + inputSectionPos + " outside of this quadNode's pos: " + this.sectionPos + ", this node's blockPos: " + this.sectionPos.convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " block width: " + this.sectionPos.getWidth().toBlockWidth() + " input detail level: " + inputSectionPos.convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " width: " + inputSectionPos.getWidth().toBlockWidth()); + LOGGER.error((replaceValue ? "set " : "get ") + inputSectionPos + " center block: " + inputSectionPos.getCenterBlockPos() + ", this pos: " + this.sectionPos + " this center block: " + this.sectionPos.getCenterBlockPos()); + throw new IllegalArgumentException("Input section pos " + inputSectionPos + " outside of this quadNode's pos: " + this.sectionPos + ", this node's blockPos: " + this.sectionPos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " block width: " + this.sectionPos.getBlockWidth() + " input detail level: " + inputSectionPos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " width: " + inputSectionPos.getBlockWidth()); } - if (inputSectionPos.sectionDetailLevel > this.sectionPos.sectionDetailLevel) + if (inputSectionPos.getDetailLevel() > this.sectionPos.getDetailLevel()) { - throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + this.sectionPos.sectionDetailLevel + " input detail level: " + inputSectionPos.sectionDetailLevel); + throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + this.sectionPos.getDetailLevel() + " input detail level: " + inputSectionPos.getDetailLevel()); } - if (inputSectionPos.sectionDetailLevel == this.sectionPos.sectionDetailLevel && !inputSectionPos.equals(this.sectionPos)) + if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel() && !inputSectionPos.equals(this.sectionPos)) { throw new IllegalArgumentException("Node and input detail level are equal, however positions are not; this tree doesn't contain the requested position. Node pos: " + this.sectionPos + ", input pos: " + inputSectionPos); } - if (inputSectionPos.sectionDetailLevel < this.minimumDetailLevel) + if (inputSectionPos.getDetailLevel() < this.minimumDetailLevel) { throw new IllegalArgumentException("Input position is requesting a detail level lower than what this node can provide. Node minimum detail level: " + this.minimumDetailLevel + ", input pos: " + inputSectionPos); } @@ -201,7 +201,7 @@ public class QuadNode // get/set logic - if (inputSectionPos.sectionDetailLevel == this.sectionPos.sectionDetailLevel) + if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel()) { // this node is the requested position if (replaceValue) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java index 2014e4039..9dcaf72e7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java @@ -119,51 +119,51 @@ public class QuadTree /** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */ protected final QuadNode getOrSetNode(DhSectionPos pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException { - if (!runBoundaryChecks || this.isSectionPosInBounds(pos)) - { - DhSectionPos rootPos = pos.convertToDetailLevel(this.treeMinDetailLevel); - int ringListPosX = rootPos.sectionX; - int ringListPosZ = rootPos.sectionZ; - - QuadNode topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ); - if (topQuadNode == null) - { - if (!setNewValue) - { - return null; - } - - topQuadNode = new QuadNode(rootPos, this.treeMaxDetailLevel); - boolean successfullyAdded = this.topRingList.set(ringListPosX, ringListPosZ, topQuadNode); - LodUtil.assertTrue(successfullyAdded, "Failed to add top quadTree node at position: " + rootPos); - } - - if (!topQuadNode.sectionPos.contains(pos)) - { - LodUtil.assertNotReach("failed to get a root node that contains the input position: " + pos + " root node pos: " + topQuadNode.sectionPos); - } - - - QuadNode returnNode = topQuadNode.getNode(pos); - if (setNewValue) - { - topQuadNode.setValue(pos, newValue); - } - return returnNode; - } - else + if (runBoundaryChecks && !this.isSectionPosInBounds(pos)) { int radius = this.diameterInBlocks() / 2; DhBlockPos2D minPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius)); DhBlockPos2D maxPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius)); - throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeMaxDetailLevel + ", max detail level: " + this.treeMinDetailLevel + ". Given Position: " + pos + " = block pos: " + pos.convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL)); + throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeMaxDetailLevel + ", max detail level: " + this.treeMinDetailLevel + ". Given Position: " + pos + " = block pos: " + pos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL)); } + + + + DhSectionPos rootPos = pos.convertNewToDetailLevel(this.treeMinDetailLevel); + int ringListPosX = rootPos.getX(); + int ringListPosZ = rootPos.getZ(); + + QuadNode topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ); + if (topQuadNode == null) + { + if (!setNewValue) + { + return null; + } + + topQuadNode = new QuadNode(rootPos, this.treeMaxDetailLevel); + boolean successfullyAdded = this.topRingList.set(ringListPosX, ringListPosZ, topQuadNode); + LodUtil.assertTrue(successfullyAdded, "Failed to add top quadTree node at position: " + rootPos); + } + + if (!topQuadNode.sectionPos.contains(pos)) + { + LodUtil.assertNotReach("failed to get a root node that contains the input position: " + pos + " root node pos: " + topQuadNode.sectionPos); + } + + + QuadNode returnNode = topQuadNode.getNode(pos); + if (setNewValue) + { + topQuadNode.setValue(pos, newValue); + } + return returnNode; } public boolean isSectionPosInBounds(DhSectionPos testPos) { // check if the testPos is within the detail level limits of the tree - boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= testPos.sectionDetailLevel && testPos.sectionDetailLevel <= this.treeMinDetailLevel; + boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= testPos.getDetailLevel() && testPos.getDetailLevel() <= this.treeMinDetailLevel; if (!detailLevelWithinBounds) { return false; @@ -174,9 +174,9 @@ public class QuadTree DhBlockPos2D treeBlockCorner = this.centerBlockPos.add(new DhBlockPos2D(-this.widthInBlocks / 2, -this.widthInBlocks / 2)); DhLodPos treeCornerPos = new DhLodPos((byte) 0, treeBlockCorner.x, treeBlockCorner.z); - DhSectionPos inputSectionCorner = testPos.convertToDetailLevel((byte) 0); - DhLodPos inputCornerPos = new DhLodPos((byte) 0, inputSectionCorner.sectionX, inputSectionCorner.sectionZ); - int inputBlockWidth = BitShiftUtil.powerOfTwo(testPos.sectionDetailLevel); + DhSectionPos inputSectionCorner = testPos.convertNewToDetailLevel((byte) 0); + DhLodPos inputCornerPos = new DhLodPos((byte) 0, inputSectionCorner.getX(), inputSectionCorner.getZ()); + int inputBlockWidth = BitShiftUtil.powerOfTwo(testPos.getDetailLevel()); return DoSquaresOverlap(treeCornerPos, this.widthInBlocks, inputCornerPos, inputBlockWidth); } @@ -404,6 +404,7 @@ public class QuadTree { if (node != null || includeNullNodes) { + // TODO can these DhSectionPos be pooled? DhSectionPos rootPos = new DhSectionPos(QuadTree.this.treeMinDetailLevel, pos2D.x, pos2D.y); if (QuadTree.this.isSectionPosInBounds(rootPos)) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java index 77dda14d1..639f8e40a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java @@ -36,7 +36,7 @@ public class QuadNodeChildIndexIterator implements Iterator public QuadNodeChildIndexIterator(QuadNode parentNode, boolean returnNullChildPos) { // only get the children if this section isn't at the bottom of the tree - if (parentNode.sectionPos.sectionDetailLevel > parentNode.minimumDetailLevel) + if (parentNode.sectionPos.getDetailLevel() > parentNode.minimumDetailLevel) { // go over each child pos for (int i = 0; i < 4; i++) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java index b36b4167f..13745870e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java @@ -46,7 +46,7 @@ public class QuadTreeNodeIterator implements Iterator> this.onlyReturnLeafValues = onlyReturnLeafValues; // TODO the naming conversion for these are flipped in a lot of places this.highestDetailLevel = rootNode.minimumDetailLevel; - this.iteratorDetailLevel = rootNode.sectionPos.sectionDetailLevel; + this.iteratorDetailLevel = rootNode.sectionPos.getDetailLevel(); if (!this.onlyReturnLeafValues) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index e1c78d4ea..8d8f428a7 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -429,6 +429,14 @@ "OpenGL Error Handling Mode", "distanthorizons.config.client.advanced.debugging.glErrorHandlingMode.@tooltip": "Defines how OpenGL errors are handled. \nMay incorrectly catch OpenGL errors thrown by other mods.", + "distanthorizons.config.client.advanced.debugging.glProfileMode": + "OpenGL Profile Mode", + "distanthorizons.config.client.advanced.debugging.glProfileMode.@tooltip": + "Requires rebooting Minecraft to apply.", + "distanthorizons.config.client.advanced.debugging.glForwardCompatibilityMode": + "OpenGL Forward Compatibility Mode", + "distanthorizons.config.client.advanced.debugging.glForwardCompatibilityMode.@tooltip": + "Requires rebooting Minecraft to apply.", "distanthorizons.config.client.advanced.buffers": @@ -736,6 +744,13 @@ "distanthorizons.config.enum.EGLErrorHandlingMode.LOG_THROW": "Log-Throw", + "distanthorizons.config.enum.EGlProfileMode.CORE": + "Core", + "distanthorizons.config.enum.EGlProfileMode.COMPAT": + "Compat", + "distanthorizons.config.enum.EGlProfileMode.ANY": + "Any", + "distanthorizons.config.enum.ELoggerMode.DISABLED": "Disabled", "distanthorizons.config.enum.ELoggerMode.LOG_ALL_TO_FILE": diff --git a/core/src/test/java/tests/DhSectionPosTest.java b/core/src/test/java/tests/DhSectionPosTest.java index 6728db56e..ff2419d1b 100644 --- a/core/src/test/java/tests/DhSectionPosTest.java +++ b/core/src/test/java/tests/DhSectionPosTest.java @@ -19,8 +19,7 @@ package tests; -import com.seibel.distanthorizons.core.pos.DhLodPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.pos.*; import org.junit.Assert; import org.junit.Test; @@ -84,22 +83,22 @@ public class DhSectionPosTest public void ParentPosTest() { DhSectionPos leaf = new DhSectionPos((byte) 0, 0, 0); - DhSectionPos convert = leaf.convertToDetailLevel((byte) 1); + DhSectionPos convert = leaf.convertNewToDetailLevel((byte) 1); DhSectionPos parent = leaf.getParentPos(); Assert.assertEquals("get parent at 0,0 fail", convert, parent); leaf = new DhSectionPos((byte) 0, 1, 1); - convert = leaf.convertToDetailLevel((byte) 1); + convert = leaf.convertNewToDetailLevel((byte) 1); parent = leaf.getParentPos(); Assert.assertEquals("get parent at 1,1 fail", convert, parent); leaf = new DhSectionPos((byte) 1, 2, 2); - convert = leaf.convertToDetailLevel((byte) 2); + convert = leaf.convertNewToDetailLevel((byte) 2); parent = leaf.getParentPos(); Assert.assertEquals("parent upscale fail", convert, parent); - convert = leaf.convertToDetailLevel((byte) 0); + convert = leaf.convertNewToDetailLevel((byte) 0); DhSectionPos childIndex = leaf.getChildByIndex(0); Assert.assertEquals("child detail fail", convert, childIndex); @@ -131,17 +130,17 @@ public class DhSectionPosTest public void GetCenterTest() { DhSectionPos node = new DhSectionPos((byte) 1, 2303, 0); - DhLodPos centerNode = node.getCenter(); - DhLodPos expectedCenterNode = new DhLodPos((byte) 0, 4606, 0); - Assert.assertEquals("", expectedCenterNode, centerNode); + DhBlockPos2D centerBlockPos = node.getCenterBlockPos(); + DhBlockPos2D expectedCenterNode = new DhBlockPos2D(4606, 0); + Assert.assertEquals("", expectedCenterNode, centerBlockPos); node = new DhSectionPos((byte) 10, 0, 0); // 1024 blocks wide - centerNode = node.getCenter(); - expectedCenterNode = new DhLodPos((byte) 0, 1024 / 2, 1024 / 2); - Assert.assertEquals("", expectedCenterNode, centerNode); - + centerBlockPos = node.getCenterBlockPos(); + expectedCenterNode = new DhBlockPos2D(1024 / 2, 1024 / 2); + Assert.assertEquals("", expectedCenterNode, centerBlockPos); + } @Test @@ -151,12 +150,213 @@ public class DhSectionPosTest DhSectionPos inputPos = new DhSectionPos((byte) 0, 4606, 0); // width 1 block Assert.assertTrue(parentNode.contains(inputPos)); - DhLodPos parentCenter = parentNode.getCenter(); - DhLodPos inputCenter = inputPos.getCenter(); + DhBlockPos2D parentCenter = parentNode.getCenterBlockPos(); + DhBlockPos2D inputCenter = inputPos.getCenterBlockPos(); - Assert.assertEquals(new DhLodPos((byte) 0, 4606, 2), parentCenter); - Assert.assertEquals(new DhLodPos((byte) 0, 4606, 0), inputCenter); + Assert.assertEquals(new DhBlockPos2D(4606, 2), parentCenter); + Assert.assertEquals(new DhBlockPos2D(4606, 0), inputCenter); } + @Test + public void CreateFromBlockPos() + { + // origin pos // + + DhBlockPos originBlockPos = new DhBlockPos(0, 0, 0); + DhSectionPos originSectionPos = new DhSectionPos(originBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); + + + // offset pos // + + DhBlockPos offsetBlockPos = new DhBlockPos(1000, 0, 42000); + DhSectionPos offsetSectionPos = new DhSectionPos(offsetBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); + + offsetBlockPos = new DhBlockPos(-987654, 0, 46); + offsetSectionPos = new DhSectionPos(offsetBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); + + } + + @Test + public void CreateFromBlockPos2D() + { + // origin pos // + + DhBlockPos2D originBlockPos = new DhBlockPos2D(0, 0); + DhSectionPos originSectionPos = new DhSectionPos(originBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); + + + // offset pos // + + DhBlockPos2D offsetBlockPos = new DhBlockPos2D(1000, 42000); + DhSectionPos offsetSectionPos = new DhSectionPos(offsetBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); + + offsetBlockPos = new DhBlockPos2D(-987654, 46); + offsetSectionPos = new DhSectionPos(offsetBlockPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); + + } + + @Test + public void CreateFromChunkPos() + { + // origin pos // + + DhChunkPos originChunkPos = new DhChunkPos(0,0); + DhSectionPos originSectionPos = new DhSectionPos(originChunkPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 0, 0), originSectionPos); + + + // offset pos // + + DhChunkPos offsetChunkPos = new DhChunkPos(1000, 42000); + DhSectionPos offsetSectionPos = new DhSectionPos(offsetChunkPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 15, 656), offsetSectionPos); + + offsetChunkPos = new DhChunkPos(-987654, 46); + offsetSectionPos = new DhSectionPos(offsetChunkPos); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); + + } + + @Test + public void ConvertToDetailLevel() + { + // origin pos // + + DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); + + originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(new DhSectionPos((byte) 1, 0, 0), originSectionPos); + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, 0, 0), originSectionPos); + + + // offset pos // + + DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + + sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(new DhSectionPos((byte) 1, -5000, 2500), sectionPos); + + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -157, 78), sectionPos); + + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, -1, 0), sectionPos); + + } + + @Test + public void GetOffsetWidth() + { + DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); + DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + + + + // 1 -> 0 + byte returnDetailLevel = 0; + originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + + // 2 -> 1 + returnDetailLevel = 1; + originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 2); + Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + sectionPos = sectionPos.convertNewToDetailLevel((byte) 2); + Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + + // Block -> 0 + returnDetailLevel = 0; + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(64, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(64, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + + // Region -> 3 + returnDetailLevel = 3; + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(4096, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(4096, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + + } + + @Test + public void GetBlockWidth() + { + DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); + DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + + + Assert.assertEquals(1, originSectionPos.getBlockWidth()); + Assert.assertEquals(1, sectionPos.getBlockWidth()); + + originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(2, originSectionPos.getBlockWidth()); + sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(2, sectionPos.getBlockWidth()); + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(64, originSectionPos.getBlockWidth()); + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(64, sectionPos.getBlockWidth()); + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(32768, originSectionPos.getBlockWidth()); + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(32768, sectionPos.getBlockWidth()); + + } + + @Test + public void GetCenterBlockPos() + { + DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); + DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + + + Assert.assertEquals(new DhBlockPos2D(0, 0), originSectionPos.getCenterBlockPos()); + Assert.assertEquals(new DhBlockPos2D(-10000, 5000), sectionPos.getCenterBlockPos()); + + + originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(new DhBlockPos2D(0, 0), originSectionPos.getCenterBlockPos()); + sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); + Assert.assertEquals(new DhBlockPos2D(-10000, 5000), sectionPos.getCenterBlockPos()); + + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(32, 32), originSectionPos.getCenterBlockPos()); + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(-10016, 5024), sectionPos.getCenterBlockPos()); + + + originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(16384, 16384), originSectionPos.getCenterBlockPos()); + sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(-16384, 16384), sectionPos.getCenterBlockPos()); + + } + + } diff --git a/core/src/test/java/tests/QuadTreeTest.java b/core/src/test/java/tests/QuadTreeTest.java index ea79698af..722dfbaec 100644 --- a/core/src/test/java/tests/QuadTreeTest.java +++ b/core/src/test/java/tests/QuadTreeTest.java @@ -649,7 +649,7 @@ public class QuadTreeTest DhSectionPos sectionPos = directChildIterator.next(); QuadNode childNode = node.getNode(sectionPos); - Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + childNode.sectionPos.sectionDetailLevel, childNode.sectionPos.sectionDetailLevel >= minDetailLevel); + Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + childNode.sectionPos.getDetailLevel(), childNode.sectionPos.getDetailLevel() >= minDetailLevel); recursivelyCreateNodeChildren(childNode, minDetailLevel, minimumDetailLevelReachedRef); childNodesIterated = true; @@ -657,9 +657,9 @@ public class QuadTreeTest // keep track of how far down the tree we have gone - if (node.sectionPos.sectionDetailLevel < minimumDetailLevelReachedRef.get()) + if (node.sectionPos.getDetailLevel() < minimumDetailLevelReachedRef.get()) { - minimumDetailLevelReachedRef.set(node.sectionPos.sectionDetailLevel); + minimumDetailLevelReachedRef.set(node.sectionPos.getDetailLevel()); } @@ -667,11 +667,11 @@ public class QuadTreeTest // assertions if (childNodesCreated) { - Assert.assertTrue("node children created below minimum detail level", node.sectionPos.sectionDetailLevel >= minDetailLevel); + Assert.assertTrue("node children created below minimum detail level", node.sectionPos.getDetailLevel() >= minDetailLevel); } if (childNodesIterated) { - Assert.assertTrue("node children iterated below minimum detail level", node.sectionPos.sectionDetailLevel - 1 >= minDetailLevel); + Assert.assertTrue("node children iterated below minimum detail level", node.sectionPos.getDetailLevel() - 1 >= minDetailLevel); } }