This commit is contained in:
s809
2023-09-18 13:24:43 +05:00
39 changed files with 1154 additions and 473 deletions
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.api.enums.config;
/**
* @since API 1.0.0
*/
public enum EGlProfileMode
{
CORE,
COMPAT,
ANY;
}
@@ -30,7 +30,7 @@ public class BitShiftUtil
{
/**
* Equivalent to: <br>
* {@literal 1 << value0, } <br>
* {@literal 1 << value, } <br>
* 2^value, <br>
* Math.pow(2, value) <br><br>
*
@@ -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();
}
}
@@ -1176,21 +1176,35 @@ public class Config
+ "")
.build();
// TODO temporary test, remove me
public static ConfigEntry<Boolean> skipChunkLoadUpdates = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment("")
.build();
// TODO temporary test, remove me
public static ConfigEntry<Boolean> skipChunkUnloadUpdates = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment("")
.build();
// TODO temporary test, remove me
public static ConfigEntry<Boolean> skipFullDataUpdateQueue = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment("")
.build();
// TODO temporary test, remove me
public static ConfigEntry<EGlProfileMode> glProfileMode = new ConfigEntry.Builder<EGlProfileMode>()
.set(EGlProfileMode.CORE)
.comment("")
.build();
// TODO temporary test, remove me
public static ConfigEntry<Boolean> glForwardCompatibilityMode = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment("")
.build();
@@ -47,15 +47,17 @@ public class FullDataDownSampler
ArrayList<CompletableFuture<IFullDataSource>> 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<IFullDataSource> 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<IFullDataSource> 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;
@@ -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(); }
}
@@ -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;
@@ -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;
@@ -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;
}
@@ -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;
@@ -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. <br><br>
* Handles loading and parsing {@link RenderDataMetaFile}s to create {@link ColumnRenderSource}s. <br><br>
*
* 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;
@@ -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++)
@@ -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
@@ -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();
@@ -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 <https://www.gnu.org/licenses/>.
*/
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<IFullDataSource> FULL_DATA_GARBAGE_COLLECTED_QUEUE = new ReferenceQueue<>();
private static final ReferenceQueue<ColumnRenderSource> RENDER_DATA_GARBAGE_COLLECTED_QUEUE = new ReferenceQueue<>();
// TODO using a ConcurrentHashMap may or may not be the best choice here
private static final Set<FullDataSourceSoftRef> FULL_DATA_SOFT_REFS = ConcurrentHashMap.newKeySet();
private static final Set<RenderDataSourceSoftRef> 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<FullDataMetaFile, IFullDataSource>
{
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<RenderDataMetaFile, ColumnRenderSource>
{
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<TMetaFile extends AbstractMetaDataContainerFile, TDataSource> extends SoftReference<TDataSource> implements Closeable
{
public final TMetaFile metaFile;
public final long createdMsTime;
private long expirationMsTime;
public AbstractDataSourceSoftTracker(TMetaFile metaFile, TDataSource dataSource, ReferenceQueue<TDataSource> 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(); }
}
}
@@ -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<IFullDataSource> 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<FullDataMetaFile> preexistingFiles, ArrayList<DhSectionPos> 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);
}
@@ -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<IFullDataSource> cachedFullDataSourceRef = new SoftReference<>(null);
private DataSourceReferenceTracker.FullDataSourceSoftRef cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this,null);
private final AtomicReference<CompletableFuture<IFullDataSource>> 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);
@@ -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);
@@ -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
@@ -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<ColumnRenderSource> cachedRenderDataSource = new SoftReference<>(null);
private DataSourceReferenceTracker.RenderDataSourceSoftRef cachedRenderDataSource = new DataSourceReferenceTracker.RenderDataSourceSoftRef(this, null);
private final AtomicReference<CompletableFuture<ColumnRenderSource>> 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<ColumnRenderSource> 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<ColumnRenderSource> getCachedDataSourceAsync(boolean updateRenderSourceCache)
{
@@ -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<DhSectionPos, File> unloadedFileBySectionPos = new ConcurrentHashMap<>();
/** contains the loaded {@link RenderMetaDataFile}'s */
private final ConcurrentHashMap<DhSectionPos, RenderMetaDataFile> metaFileBySectionPos = new ConcurrentHashMap<>();
/** contains the loaded {@link RenderDataMetaFile}'s */
private final ConcurrentHashMap<DhSectionPos, RenderDataMetaFile> 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<File> 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<CompletableFuture<Void>> 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); }
@@ -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<CompletableFuture<WorldGenResult>> 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<WorldGenResult> 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);
}
@@ -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);
}
}
@@ -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
@@ -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); }
}
@@ -47,8 +47,7 @@ public class DhLodPos implements Comparable<DhLodPos>
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<DhLodPos>
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));
}
@@ -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: <br>
* 0 -> 1 <br>
* 1 -> 2 <br>
* 2 -> 4 <br>
* 3 -> 8 <br>
* 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 <br><br>
*
@@ -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<DhSectionPos> 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<DhSectionPos> 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. <br>
* 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();
}
}
@@ -143,7 +143,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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
@@ -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(
@@ -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<LoadedRenderBuffer> 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
@@ -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)
@@ -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<Path> pathStream = Files.walk(saveStructure.getRenderCacheFolder(levelWrapper).toPath(), MAX_SCAN_DEPTH))
{
List<File> 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);
@@ -179,21 +179,21 @@ public class QuadNode<T>
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<T>
// get/set logic
if (inputSectionPos.sectionDetailLevel == this.sectionPos.sectionDetailLevel)
if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel())
{
// this node is the requested position
if (replaceValue)
@@ -119,51 +119,51 @@ public class QuadTree<T>
/** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */
protected final QuadNode<T> 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<T> topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ);
if (topQuadNode == null)
{
if (!setNewValue)
{
return null;
}
topQuadNode = new QuadNode<T>(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<T> 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<T> topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ);
if (topQuadNode == null)
{
if (!setNewValue)
{
return null;
}
topQuadNode = new QuadNode<T>(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<T> 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<T>
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<T>
{
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))
{
@@ -36,7 +36,7 @@ public class QuadNodeChildIndexIterator<T> implements Iterator<Integer>
public QuadNodeChildIndexIterator(QuadNode<T> 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++)
@@ -46,7 +46,7 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
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)
@@ -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":
+217 -17
View File
@@ -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());
}
}
+5 -5
View File
@@ -649,7 +649,7 @@ public class QuadTreeTest
DhSectionPos sectionPos = directChildIterator.next();
QuadNode<Integer> 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);
}
}