diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java index e3f4f5210..d08989ad8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java @@ -205,7 +205,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo byte sectionDetailLevel = (byte) (requestedDetailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); // get the positions for this request - DhSectionPos sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel); + long sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel); DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel(); @@ -215,7 +215,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get(); if (dataSource == null) { - return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + sectionPos + "]."); + return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "]."); } else { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 229fbbea7..5cb41981e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -67,7 +67,7 @@ public class FullDataPointIdMap private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** should only be used for debugging */ - private DhSectionPos pos; + private long pos; /** The index should be the same as the Entry's ID */ private final ArrayList entryList = new ArrayList<>(); @@ -79,7 +79,7 @@ public class FullDataPointIdMap // constructor // //=============// - public FullDataPointIdMap(DhSectionPos pos) { this.pos = pos; } + public FullDataPointIdMap(long pos) { this.pos = pos; } @@ -123,7 +123,7 @@ public class FullDataPointIdMap public boolean isEmpty() { return this.entryList.isEmpty(); } - public DhSectionPos getPos() { return this.pos; } + public long getPos() { return this.pos; } @@ -270,7 +270,7 @@ public class FullDataPointIdMap } /** Should only be used if this map is going to be reused, otherwise bad things will happen. */ - public void clear(DhSectionPos pos) + public void clear(long pos) { this.pos = pos; this.entryList.clear(); @@ -321,7 +321,7 @@ public class FullDataPointIdMap } /** Creates a new IdBiomeBlockStateMap from the given UTF formatted stream */ - public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, DhSectionPos pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException + public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, long pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException { int entityCount = inputStream.readInt(); if (entityCount < 0) @@ -361,12 +361,10 @@ public class FullDataPointIdMap } } - //LOGGER.trace("deserialized " + pos + " " + newMap.entryList.size() + "-" + entityCount); - if (newMap.size() != entityCount) { // if the mappings are out of sync then the LODs will render incorrectly due to IDs being wrong - LodUtil.assertNotReach("ID maps failed to deserialize for pos: "+pos+", incorrect entity count. Expected count ["+entityCount+"], actual count ["+newMap.size()+"]"); + LodUtil.assertNotReach("ID maps failed to deserialize for pos: ["+ DhSectionPos.toString(pos)+"], incorrect entity count. Expected count ["+entityCount+"], actual count ["+newMap.size()+"]"); } return newMap; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV1.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV1.java index 4f782837b..bc82aa94a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV1.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV1.java @@ -75,7 +75,7 @@ public class FullDataSourceV1 implements IDataSource /** A flattened 2D array (for the X and Z directions) containing an array for the Y direction. */ private final long[][] dataArrays; - private DhSectionPos sectionPos; + private long sectionPos; private boolean isEmpty = true; @@ -85,8 +85,8 @@ public class FullDataSourceV1 implements IDataSource // constructors // //==============// - public static FullDataSourceV1 createEmpty(DhSectionPos pos) { return new FullDataSourceV1(pos); } - private FullDataSourceV1(DhSectionPos sectionPos) + public static FullDataSourceV1 createEmpty(long pos) { return new FullDataSourceV1(pos); } + private FullDataSourceV1(long sectionPos) { this.dataArrays = new long[WIDTH * WIDTH][0]; this.mapping = new FullDataPointIdMap(sectionPos); @@ -111,19 +111,19 @@ public class FullDataSourceV1 implements IDataSource //=====================// @Override - public DhSectionPos getKey() { return this.sectionPos; } + public Long getKey() { return this.sectionPos; } @Override - public DhSectionPos getPos() { return this.sectionPos; } + public Long getPos() { return this.sectionPos; } - public void resizeDataStructuresForRepopulation(DhSectionPos pos) + public void resizeDataStructuresForRepopulation(long pos) { // no data structures need to be changed, only the source's position this.sectionPos = pos; } @Override - public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); } + public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.sectionPos) - SECTION_SIZE_OFFSET); } public boolean isEmpty() { return this.isEmpty; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java index 6db6b6411..e95957c62 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java @@ -71,9 +71,9 @@ public class FullDataSourceV2 implements IDataSource private int cachedHashCode = 0; - private DhSectionPos pos; + private long pos; @Override - public DhSectionPos getKey() { return this.pos; } + public Long getKey() { return this.pos; } public final FullDataPointIdMap mapping; @@ -112,8 +112,8 @@ public class FullDataSourceV2 implements IDataSource // constructors // //==============// - public static FullDataSourceV2 createEmpty(DhSectionPos pos) { return new FullDataSourceV2(pos); } - private FullDataSourceV2(DhSectionPos pos) + public static FullDataSourceV2 createEmpty(long pos) { return new FullDataSourceV2(pos); } + private FullDataSourceV2(long pos) { this.pos = pos; this.dataPoints = new LongArrayList[WIDTH * WIDTH]; @@ -126,8 +126,8 @@ public class FullDataSourceV2 implements IDataSource this.columnWorldCompressionMode = new byte[WIDTH * WIDTH]; } - public static FullDataSourceV2 createWithData(DhSectionPos pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationStep, byte[] columnWorldCompressionMode) { return new FullDataSourceV2(pos, mapping, data, columnGenerationStep, columnWorldCompressionMode); } - private FullDataSourceV2(DhSectionPos pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationSteps, byte[] columnWorldCompressionMode) + public static FullDataSourceV2 createWithData(long pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationStep, byte[] columnWorldCompressionMode) { return new FullDataSourceV2(pos, mapping, data, columnGenerationStep, columnWorldCompressionMode); } + private FullDataSourceV2(long pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationSteps, byte[] columnWorldCompressionMode) { LodUtil.assertTrue(data.length == WIDTH * WIDTH); @@ -230,8 +230,8 @@ public class FullDataSourceV2 implements IDataSource } - byte thisDetailLevel = this.pos.getDetailLevel(); - byte inputDetailLevel = inputDataSource.pos.getDetailLevel(); + byte thisDetailLevel = DhSectionPos.getDetailLevel(this.pos); + byte inputDetailLevel = DhSectionPos.getDetailLevel(inputDataSource.pos); // determine the mapping changes necessary for the input to map onto this datasource @@ -256,7 +256,7 @@ public class FullDataSourceV2 implements IDataSource } // determine if this data source should be applied to its parent - this.applyToParent = (dataChanged && this.pos.getDetailLevel() < AbstractDataSourceHandler.TOP_SECTION_DETAIL_LEVEL); + this.applyToParent = (dataChanged && DhSectionPos.getDetailLevel(this.pos) < AbstractDataSourceHandler.TOP_SECTION_DETAIL_LEVEL); if (dataChanged) { @@ -269,9 +269,9 @@ public class FullDataSourceV2 implements IDataSource public boolean updateFromSameDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds) { // both data sources should have the same detail level - if (inputDataSource.pos.getDetailLevel() != this.pos.getDetailLevel()) + if (DhSectionPos.getDetailLevel(inputDataSource.pos) != DhSectionPos.getDetailLevel(this.pos)) { - throw new IllegalArgumentException("Both data sources must have the same detail level. Expected ["+this.pos.getDetailLevel()+"], received ["+inputDataSource.pos.getDetailLevel()+"]."); + throw new IllegalArgumentException("Both data sources must have the same detail level. Expected ["+ DhSectionPos.getDetailLevel(this.pos)+"], received ["+ DhSectionPos.getDetailLevel(inputDataSource.pos)+"]."); } // copy over everything from the input data source into this one @@ -351,9 +351,9 @@ public class FullDataSourceV2 implements IDataSource } public boolean updateFromOneBelowDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds) { - if (inputDataSource.pos.getDetailLevel() + 1 != this.pos.getDetailLevel()) + if (DhSectionPos.getDetailLevel(inputDataSource.pos) + 1 != DhSectionPos.getDetailLevel(this.pos)) { - throw new IllegalArgumentException("Input data source must be exactly 1 detail level below this data source. Expected [" + (this.pos.getDetailLevel() - 1) + "], received [" + inputDataSource.pos.getDetailLevel() + "]."); + throw new IllegalArgumentException("Input data source must be exactly 1 detail level below this data source. Expected [" + (DhSectionPos.getDetailLevel(this.pos) - 1) + "], received [" + DhSectionPos.getDetailLevel(inputDataSource.pos) + "]."); } // input is one detail level lower (higher detail) @@ -362,10 +362,10 @@ public class FullDataSourceV2 implements IDataSource // determine where in the input data source should be written to // since the input is one detail level below it will be one of this position's 4 children - int minChildXPos = this.pos.getChildByIndex(0).getX(); - int recipientOffsetX = (inputDataSource.pos.getX() == minChildXPos) ? 0 : (WIDTH / 2); - int minChildZPos = this.pos.getChildByIndex(0).getZ(); - int recipientOffsetZ = (inputDataSource.pos.getZ() == minChildZPos) ? 0 : (WIDTH / 2); + int minChildXPos = DhSectionPos.getX(DhSectionPos.getChildByIndex(this.pos, 0)); + int recipientOffsetX = (DhSectionPos.getX(inputDataSource.pos) == minChildXPos) ? 0 : (WIDTH / 2); + int minChildZPos = DhSectionPos.getZ(DhSectionPos.getChildByIndex(this.pos, 0)); + int recipientOffsetZ = (DhSectionPos.getZ(inputDataSource.pos) == minChildZPos) ? 0 : (WIDTH / 2); @@ -783,7 +783,7 @@ public class FullDataSourceV2 implements IDataSource * * @see FullDataSourceV2#dataPoints */ - public static void throwIfDataColumnInWrongOrder(DhSectionPos pos, LongArrayList dataArray) throws IllegalStateException + public static void throwIfDataColumnInWrongOrder(long pos, LongArrayList dataArray) throws IllegalStateException { long firstDataPoint = dataArray.getLong(0); int firstBottomY = FullDataPointUtil.getBottomY(firstDataPoint); @@ -793,7 +793,7 @@ public class FullDataSourceV2 implements IDataSource if (firstBottomY < lastBottomY) { - throw new IllegalStateException("Incorrect data point order at pos: "+pos+", first datapoint bottom Y ["+firstBottomY+"], last datapoint bottom Y ["+lastBottomY+"]."); + throw new IllegalStateException("Incorrect data point order at pos: ["+ DhSectionPos.toString(pos)+"], first datapoint bottom Y ["+firstBottomY+"], last datapoint bottom Y ["+lastBottomY+"]."); } } @@ -829,7 +829,7 @@ public class FullDataSourceV2 implements IDataSource // pooling // //=========// - private static void prepPooledDataSource(DhSectionPos pos, boolean clearData, FullDataSourceV2 dataSource) + private static void prepPooledDataSource(long pos, boolean clearData, FullDataSourceV2 dataSource) { dataSource.pos = pos; @@ -857,10 +857,10 @@ public class FullDataSourceV2 implements IDataSource //=====================// @Override - public DhSectionPos getPos() { return this.pos; } + public Long getPos() { return this.pos; } @Override - public byte getDataDetailLevel() { return (byte) (this.pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); } + public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); } public EDhApiWorldGenerationStep getWorldGenStepAtRelativePos(int relX, int relZ) { @@ -899,7 +899,7 @@ public class FullDataSourceV2 implements IDataSource //================// @Override - public String toString() { return this.pos.toString(); } + public String toString() { return DhSectionPos.toString(this.pos); } @Override public int hashCode() @@ -912,7 +912,7 @@ public class FullDataSourceV2 implements IDataSource } private void generateHashCode() { - int result = this.pos.hashCode(); + int result = DhSectionPos.hashCode(this.pos); result = 31 * result + Arrays.deepHashCode(this.dataPoints); result = 17 * result + Arrays.hashCode(this.columnGenerationSteps); result = 43 * result + Arrays.hashCode(this.columnWorldCompressionMode); @@ -929,7 +929,7 @@ public class FullDataSourceV2 implements IDataSource } FullDataSourceV2 other = (FullDataSourceV2) obj; - if (!other.pos.equals(this.pos)) + if (other.pos != this.pos) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java index 453f5b490..92626cbdb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java @@ -59,7 +59,7 @@ public class ColumnRenderSource implements IDataSource /** will be zero if an empty data source was created */ public int verticalDataCount; - public DhSectionPos pos; + public long pos; public int yOffset; public LongArrayList renderDataContainer; @@ -77,11 +77,11 @@ public class ColumnRenderSource implements IDataSource //==============// /** - * This is separate from {@link DataSourcePool#getPooledSource(DhSectionPos, boolean)} + * This is separate from {@link DataSourcePool#getPooledSource(long, boolean)} * because we need to pass in a couple extra values, * specifically maxVerticalSize and yOffset. */ - public static ColumnRenderSource getPooledRenderSource(DhSectionPos pos, int maxVerticalSize, int yOffset, boolean clearData) + public static ColumnRenderSource getPooledRenderSource(long pos, int maxVerticalSize, int yOffset, boolean clearData) { ColumnRenderSource renderSource = DATA_SOURCE_POOL.getPooledSource(pos); @@ -109,14 +109,14 @@ public class ColumnRenderSource implements IDataSource } - private static ColumnRenderSource createEmptyRenderSource(DhSectionPos sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); } + private static ColumnRenderSource createEmptyRenderSource(long sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); } /** * Creates an empty ColumnRenderSource. * * @param pos the relative position of the container * @param maxVerticalSize the maximum vertical size of the container */ - private ColumnRenderSource(DhSectionPos pos, int maxVerticalSize, int yOffset) + private ColumnRenderSource(long pos, int maxVerticalSize, int yOffset) { this.verticalDataCount = maxVerticalSize; this.renderDataContainer = new LongArrayList(new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount]); @@ -155,7 +155,7 @@ public class ColumnRenderSource implements IDataSource final String errorMessagePrefix = "Unable to complete update for RenderSource pos: [" + this.pos + "] and pos: [" + inputFullDataSource.getPos() + "]. Error:"; boolean dataChanged = false; - if (inputFullDataSource.getPos().getDetailLevel() == this.pos.getDetailLevel()) + if (DhSectionPos.getDetailLevel(inputFullDataSource.getPos()) == DhSectionPos.getDetailLevel(this.pos)) { try { @@ -167,8 +167,8 @@ public class ColumnRenderSource implements IDataSource - DhBlockPos2D centerBlockPos = inputFullDataSource.getPos().getCenterBlockPos(); - int halfBlockWidth = inputFullDataSource.getPos().getBlockWidth() / 2; + DhBlockPos2D centerBlockPos = DhSectionPos.getCenterBlockPos(inputFullDataSource.getPos()); + int halfBlockWidth = DhSectionPos.getBlockWidth(inputFullDataSource.getPos()) / 2; DhBlockPos2D minBlockPos = new DhBlockPos2D(centerBlockPos.x - halfBlockWidth, centerBlockPos.z - halfBlockWidth); for (int x = 0; x < FullDataSourceV2.WIDTH; x++) @@ -215,11 +215,11 @@ public class ColumnRenderSource implements IDataSource // data helper methods // //=====================// - public DhSectionPos getPos() { return this.pos; } + public Long getPos() { return this.pos; } @Override - public DhSectionPos getKey() { return this.pos; } + public Long getKey() { return this.pos; } - public byte getDataDetailLevel() { return (byte) (this.pos.getDetailLevel() - SECTION_SIZE_OFFSET); } + public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - SECTION_SIZE_OFFSET); } public boolean isEmpty() { return this.isEmpty; } public void markNotEmpty() { this.isEmpty = false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index aad029d2b..02c064408 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -98,7 +98,7 @@ public class ColumnRenderBufferBuilder && !clientLevel.getLevelWrapper().getDimensionType().isTheEnd() // FIXME temporary fix // Cave culling is currently broken for any detail level above 0 - && renderSource.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL + && DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL ); int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get(); @@ -131,7 +131,7 @@ public class ColumnRenderBufferBuilder { try { - ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.pos.getMinCornerLodPos().getCornerBlockPos(), clientLevel.getMinY())); + ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(renderSource.pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(renderSource.pos))); try { buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); @@ -174,9 +174,9 @@ public class ColumnRenderBufferBuilder boolean enableColumnBufferLimit = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get(); if (enableColumnBufferLimit) { - if (renderSource.pos.getDetailLevel() == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get() - && renderSource.pos.getX() == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get() - && renderSource.pos.getZ() == Config.Client.Advanced.Debugging.columnBuilderDebugZPos.get()) + if (DhSectionPos.getDetailLevel(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get() + && DhSectionPos.getX(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get() + && DhSectionPos.getZ(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugZPos.get()) { int test = 0; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/IColumnDataView.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/IColumnDataView.java index 825824162..2d97e9426 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/IColumnDataView.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/columnViews/IColumnDataView.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.dataObjects.render.columnViews; +import it.unimi.dsi.fastutil.longs.LongIterator; + import java.util.Iterator; public interface IColumnDataView @@ -28,18 +30,18 @@ public interface IColumnDataView // FIXME probably horizontal size in blocks? int size(); - default Iterator iterator() + default LongIterator iterator() { - return new Iterator() + return new LongIterator() { private int index = 0; - private final int size = size(); + private final int size = IColumnDataView.this.size(); @Override public boolean hasNext() { return this.index < this.size; } @Override - public Long next() { return get(this.index++); } + public long nextLong() { return IColumnDataView.this.get(this.index++); } }; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index f66e26262..3f0e29f52 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import org.apache.logging.log4j.Logger; import java.util.HashSet; @@ -52,7 +53,7 @@ public class FullDataToRenderDataTransformer private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private static final HashSet brokenPos = new HashSet<>(); + private static final LongOpenHashSet brokenPos = new LongOpenHashSet(); @@ -98,7 +99,7 @@ public class FullDataToRenderDataTransformer */ private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, FullDataSourceV2 fullDataSource) throws InterruptedException { - final DhSectionPos pos = fullDataSource.getPos(); + final long pos = fullDataSource.getPos(); final byte dataDetail = fullDataSource.getDataDetailLevel(); final int vertSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(fullDataSource.getDataDetailLevel()); final ColumnRenderSource columnSource = ColumnRenderSource.getPooledRenderSource(pos, vertSize, level.getMinY(), true); @@ -111,12 +112,12 @@ public class FullDataToRenderDataTransformer if (dataDetail == columnSource.getDataDetailLevel()) { - int baseX = pos.getMinCornerLodPos().getCornerBlockPos().x; - int baseZ = pos.getMinCornerLodPos().getCornerBlockPos().z; + int baseX = DhSectionPos.getMinCornerBlockX(pos); + int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - for (int x = 0; x < pos.getWidthCountForLowerDetailedSection(dataDetail); x++) + for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) { - for (int z = 0; z < pos.getWidthCountForLowerDetailedSection(dataDetail); z++) + for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) { throwIfThreadInterrupted(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java index bc6da58f6..5f3001621 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java @@ -73,7 +73,7 @@ public class LodDataBuilder sectionPosX = (sectionPosX < 0) ? ((sectionPosX + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosX / NUMB_OF_CHUNKS_WIDE); int sectionPosZ = chunkWrapper.getChunkPos().z; sectionPosZ = (sectionPosZ < 0) ? ((sectionPosZ + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosZ / NUMB_OF_CHUNKS_WIDE); - DhSectionPos pos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); + long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos); dataSource.isEmpty = false; @@ -303,7 +303,7 @@ public class LodDataBuilder /** @throws ClassCastException if an API user returns the wrong object type(s) */ public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException { - FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(new DhSectionPos(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ))); + FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(DhSectionPos.encode(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ))); for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++) { for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java index 46446cf10..ff7cebd50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java @@ -43,11 +43,17 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3i; */ public enum EDhDirection { + /** negative Y */ DOWN(0, 1, -1, "down", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Y, new Vec3i(0, -1, 0)), + /** positive Y */ UP(1, 0, -1, "up", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Y, new Vec3i(0, 1, 0)), + /** negative Z */ NORTH(2, 3, 2, "north", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, -1)), + /** positive Z */ SOUTH(3, 2, 0, "south", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, 1)), + /** negative X */ WEST(4, 5, 1, "west", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.X, new Vec3i(-1, 0, 0)), + /** positive X */ EAST(5, 4, 3, "east", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.X, new Vec3i(1, 0, 0)); /** diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java index 207068e2b..ec6c50f85 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java @@ -17,7 +17,6 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.io.StreamCorruptedException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; @@ -27,8 +26,8 @@ import java.util.concurrent.locks.ReentrantLock; // We shouldn't need multiple data source handlers public abstract class AbstractDataSourceHandler , - TDTO extends IBaseDTO, - TRepo extends AbstractDhRepo, + TDTO extends IBaseDTO, + TRepo extends AbstractDhRepo, TDhLevel extends IDhLevel> implements AutoCloseable { @@ -56,8 +55,8 @@ public abstract class AbstractDataSourceHandler * generally just used for debugging, * keeps track of which positions are currently locked. */ - public final Set lockedPosSet = ConcurrentHashMap.newKeySet(); - public final ConcurrentHashMap queuedUpdateCountsByPos = new ConcurrentHashMap<>(); + public final Set lockedPosSet = ConcurrentHashMap.newKeySet(); + public final ConcurrentHashMap queuedUpdateCountsByPos = new ConcurrentHashMap<>(); protected final ReentrantLock closeLock = new ReentrantLock(); @@ -102,7 +101,7 @@ public abstract class AbstractDataSourceHandler protected abstract TDataSource createDataSourceFromDto(TDTO dto) throws InterruptedException, IOException, DataCorruptedException; protected abstract TDTO createDtoFromDataSource(TDataSource dataSource); - protected abstract TDataSource makeEmptyDataSource(DhSectionPos pos); + protected abstract TDataSource makeEmptyDataSource(long pos); @@ -116,7 +115,7 @@ public abstract class AbstractDataSourceHandler * * This call is concurrent. I.e. it supports being called by multiple threads at the same time. */ - public CompletableFuture getAsync(DhSectionPos pos) + public CompletableFuture getAsync(long pos) { ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor(); if (executor == null || executor.isTerminated()) @@ -138,10 +137,10 @@ public abstract class AbstractDataSourceHandler /** * Should only be used in internal file handler methods where we are already running on a file handler thread. * Can return null if the repo is in the process of being shut down - * @see AbstractDataSourceHandler#getAsync(DhSectionPos) + * @see AbstractDataSourceHandler#getAsync(long) */ @Nullable - public TDataSource get(DhSectionPos pos) + public TDataSource get(long pos) { TDataSource dataSource = null; try @@ -161,7 +160,7 @@ public abstract class AbstractDataSourceHandler // if the user is migrating from a nightly build and used ZStd. if (CORRUPT_DATA_ERRORS_LOGGED.add(e.getMessage())) { - LOGGER.warn("Corrupted data found at pos " + pos + ". Data at position will be deleted so it can be re-generated to prevent issues. Future errors with this same message won't be logged. Error: " + e.getMessage(), e); + LOGGER.warn("Corrupted data found at pos [" + DhSectionPos.toString(pos) + "]. Data at position will be deleted so it can be re-generated to prevent issues. Future errors with this same message won't be logged. Error: " + e.getMessage(), e); } this.repo.deleteWithKey(pos); @@ -178,7 +177,7 @@ public abstract class AbstractDataSourceHandler catch (InterruptedException ignore) { } catch (IOException e) { - LOGGER.warn("File read Error for pos ["+pos+"], error: "+e.getMessage(), e); + LOGGER.warn("File read Error for pos ["+ DhSectionPos.toString(pos)+"], error: "+e.getMessage(), e); } return dataSource; @@ -231,7 +230,7 @@ public abstract class AbstractDataSourceHandler * After this method returns the inputData will be written to file. * @param updatePos the position to update */ - protected void updateDataSourceAtPos(DhSectionPos updatePos, @NotNull FullDataSourceV2 inputData, boolean lockOnUpdatePos) + protected void updateDataSourceAtPos(long updatePos, @NotNull FullDataSourceV2 inputData, boolean lockOnUpdatePos) { boolean methodLocked = false; // a lock is necessary to prevent two threads from writing to the same position at once, @@ -293,7 +292,7 @@ public abstract class AbstractDataSourceHandler //================// /** used for debugging to track which positions are queued for updating */ - private void markUpdateStart(DhSectionPos dataSourcePos) + private void markUpdateStart(long dataSourcePos) { this.queuedUpdateCountsByPos.compute(dataSourcePos, (pos, atomicCount) -> { @@ -306,7 +305,7 @@ public abstract class AbstractDataSourceHandler }); } /** used for debugging to track which positions are queued for updating */ - private void markUpdateEnd(DhSectionPos dataSourcePos) + private void markUpdateEnd(long dataSourcePos) { this.queuedUpdateCountsByPos.compute(dataSourcePos, (pos, atomicCount) -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java index fa6b80fdf..cd8da6f4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/DataSourcePool.java @@ -1,8 +1,6 @@ package com.seibel.distanthorizons.core.file; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.util.ThreadUtil; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -27,7 +25,7 @@ public class DataSourcePool, TDhLevel private final ArrayList pooledDataSources = new ArrayList<>(); private final ReentrantLock poolLock = new ReentrantLock(); - private final Function createEmptyDatasourceFunc; + private final Function createEmptyDatasourceFunc; @Nullable private final IPrepPooledDataSourceFunc prepDatasourceFunc; @@ -37,7 +35,7 @@ public class DataSourcePool, TDhLevel // constructor // //=============// - public DataSourcePool(Function createEmptyDatasourceFunc, @Nullable IPrepPooledDataSourceFunc prepDatasourceFunc) + public DataSourcePool(Function createEmptyDatasourceFunc, @Nullable IPrepPooledDataSourceFunc prepDatasourceFunc) { this.createEmptyDatasourceFunc = createEmptyDatasourceFunc; this.prepDatasourceFunc = prepDatasourceFunc; @@ -51,12 +49,12 @@ public class DataSourcePool, TDhLevel /** * Returns a cleared data source. - * @see DataSourcePool#getPooledSource(DhSectionPos, boolean) + * @see DataSourcePool#getPooledSource(long, boolean) */ - public TDataSource getPooledSource(DhSectionPos pos) { return this.getPooledSource(pos, true);} + public TDataSource getPooledSource(long pos) { return this.getPooledSource(pos, true);} /** @return an empty data source if non are cached */ - public TDataSource getPooledSource(DhSectionPos pos, boolean clearData) + public TDataSource getPooledSource(long pos, boolean clearData) { try { @@ -133,7 +131,7 @@ public class DataSourcePool, TDhLevel public interface IPrepPooledDataSourceFunc, TDhLevel extends IDhLevel> { /** @param clearData will be false if the data will be immediately overwritten anyway */ - void prepDataSource(DhSectionPos pos, boolean clearData, TDataSource dataSource); + void prepDataSource(long pos, boolean clearData, TDataSource dataSource); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/IDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/file/IDataSource.java index 09d4d4ee8..3b855e729 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/IDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/IDataSource.java @@ -3,7 +3,6 @@ package com.seibel.distanthorizons.core.file; import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.sql.dto.IBaseDTO; /** @@ -13,9 +12,9 @@ import com.seibel.distanthorizons.core.sql.dto.IBaseDTO; * * @param there are times when we need specifically a client level vs a more generic level */ -public interface IDataSource extends IBaseDTO, AutoCloseable +public interface IDataSource extends IBaseDTO, AutoCloseable { - DhSectionPos getPos(); + Long getPos(); /** @return true if the data was changed */ boolean update(FullDataSourceV2 chunkData, TDhLevel level); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/DelayedFullDataSourceSaveCache.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/DelayedFullDataSourceSaveCache.java index aa87662f8..e2efce4c7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/DelayedFullDataSourceSaveCache.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/DelayedFullDataSourceSaveCache.java @@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.TimerUtil; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -22,8 +21,8 @@ public class DelayedFullDataSourceSaveCache private static final Timer DELAY_UPDATE_TIMER = TimerUtil.CreateTimer("Delayed Full Datasource Save Timer"); - public final ConcurrentHashMap dataSourceByPosition = new ConcurrentHashMap<>(); - private final ConcurrentHashMap saveTimerTasksBySectionPos = new ConcurrentHashMap<>(); + public final ConcurrentHashMap dataSourceByPosition = new ConcurrentHashMap<>(); + private final ConcurrentHashMap saveTimerTasksBySectionPos = new ConcurrentHashMap<>(); private final ISaveDataSourceFunc onSaveTimeoutFunc; private final int saveDelayInMs; @@ -48,7 +47,7 @@ public class DelayedFullDataSourceSaveCache public void queueDataSourceForUpdateAndSave(FullDataSourceV2 inputDataSource) { - DhSectionPos dataSourcePos = inputDataSource.getPos(); + long dataSourcePos = inputDataSource.getPos(); this.dataSourceByPosition.compute(dataSourcePos, (inputPos, temporaryDataSource) -> { if (temporaryDataSource == null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java index 1ee291fcc..4c1e443fe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java @@ -9,6 +9,7 @@ import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV1DTO; import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV1Repo; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; +import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -92,7 +93,7 @@ public class FullDataSourceProviderV1 * * This call is concurrent. I.e. it supports being called by multiple threads at the same time. */ - public CompletableFuture getAsync(DhSectionPos pos) + public CompletableFuture getAsync(long pos) { ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor(); if (executor == null || executor.isTerminated()) @@ -113,10 +114,10 @@ public class FullDataSourceProviderV1 /** * Should only be used in internal file handler methods where we are already running on a file handler thread. * Can return null. - * @see FullDataSourceProviderV1#getAsync(DhSectionPos) + * @see FullDataSourceProviderV1#getAsync(long) */ @Nullable - public FullDataSourceV1 get(DhSectionPos pos) + public FullDataSourceV1 get(Long pos) { FullDataSourceV1 dataSource = null; try @@ -133,12 +134,12 @@ public class FullDataSourceProviderV1 { // stack trace not included since a lot of corrupt data would cause the log to get quite messy, // and it should be fairly easy to see what the problem was from the message - LOGGER.warn("Corrupted data found at pos "+pos+". Data at position will be deleted so it can be re-generated and to prevent future issues. Error: "+e.getMessage()); + LOGGER.warn("Corrupted data found at pos ["+ DhSectionPos.toString(pos)+"]. Data at position will be deleted so it can be re-generated and to prevent future issues. Error: "+e.getMessage()); this.repo.deleteWithKey(pos); } catch (IOException e) { - LOGGER.warn("File read Error for pos ["+pos+"], error: "+e.getMessage(), e); + LOGGER.warn("File read Error for pos ["+ DhSectionPos.toString(pos)+"], error: "+e.getMessage(), e); } return dataSource; @@ -156,10 +157,10 @@ public class FullDataSourceProviderV1 { ArrayList dataSourceList = new ArrayList<>(); - ArrayList migrationPosList = this.repo.getPositionsToMigrate(limit); + LongArrayList migrationPosList = this.repo.getPositionsToMigrate(limit); for (int i = 0; i < migrationPosList.size(); i++) { - DhSectionPos pos = migrationPosList.get(i); + Long pos = migrationPosList.getLong(i); FullDataSourceV1 dataSource = this.get(pos); if (dataSource != null) { @@ -170,7 +171,7 @@ public class FullDataSourceProviderV1 return dataSourceList; } - public void markMigrationFailed(DhSectionPos pos) { ((FullDataSourceV1Repo) this.repo).markMigrationFailed(pos); } + public void markMigrationFailed(long pos) { ((FullDataSourceV1Repo) this.repo).markMigrationFailed(pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index a8fbd40c9..d2e61e34b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -49,7 +50,6 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; /** * Handles reading/writing {@link FullDataSourceV2} @@ -97,7 +97,7 @@ public class FullDataSourceProviderV2 * Tracks which positions are currently being updated * to prevent duplicate concurrent updates. */ - public final Set parentUpdatingPosSet = ConcurrentHashMap.newKeySet(); + public final Set parentUpdatingPosSet = ConcurrentHashMap.newKeySet(); // TODO only run thread if modifications happened recently /** @@ -172,7 +172,7 @@ public class FullDataSourceProviderV2 { return dto.createPooledDataSource(this.level.getLevelWrapper()); } @Override - protected FullDataSourceV2 makeEmptyDataSource(DhSectionPos pos) { return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true); } + protected FullDataSourceV2 makeEmptyDataSource(long pos) { return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true); } @@ -201,13 +201,13 @@ public class FullDataSourceProviderV2 && this.parentUpdatingPosSet.size() < MAX_UPDATE_TASK_COUNT) { // get the positions that need to be applied to their parents - ArrayList parentUpdatePosList = this.repo.getPositionsToUpdate(MAX_UPDATE_TASK_COUNT); + LongArrayList parentUpdatePosList = this.repo.getPositionsToUpdate(MAX_UPDATE_TASK_COUNT); // combine updates together based on their parent - HashMap> updatePosByParentPos = new HashMap<>(); - for (DhSectionPos pos : parentUpdatePosList) + HashMap> updatePosByParentPos = new HashMap<>(); + for (Long pos : parentUpdatePosList) { - updatePosByParentPos.compute(pos.getParentPos(), (parentPos, updatePosSet) -> + updatePosByParentPos.compute(DhSectionPos.getParentPos(pos), (parentPos, updatePosSet) -> { if (updatePosSet == null) { @@ -219,7 +219,7 @@ public class FullDataSourceProviderV2 } // queue the updates - for (DhSectionPos parentUpdatePos : updatePosByParentPos.keySet()) + for (Long parentUpdatePos : updatePosByParentPos.keySet()) { // stop if there are already a bunch of updates queued if (this.parentUpdatingPosSet.size() > MAX_UPDATE_TASK_COUNT @@ -246,7 +246,7 @@ public class FullDataSourceProviderV2 this.lockedPosSet.add(parentUpdatePos); // apply each child pos to the parent - for (DhSectionPos childPos : updatePosByParentPos.get(parentUpdatePos)) + for (Long childPos : updatePosByParentPos.get(parentUpdatePos)) { ReentrantLock childReadLock = this.updateLockProvider.getLock(childPos); try @@ -423,7 +423,7 @@ public class FullDataSourceProviderV2 } catch (Exception e) { - DhSectionPos migrationPos = legacyDataSource.getPos(); + Long migrationPos = legacyDataSource.getPos(); LOGGER.warn("Unexpected issue migrating data source at pos " + migrationPos + ". Error: " + e.getMessage(), e); this.legacyFileHandler.markMigrationFailed(migrationPos); } @@ -555,18 +555,18 @@ public class FullDataSourceProviderV2 * an empty array if all positions were generated */ @Nullable - public ArrayList getPositionsToRetrieve(DhSectionPos pos) { return null; } + public LongArrayList getPositionsToRetrieve(Long pos) { return null; } /** * Returns how many positions could potentially be generated for this position assuming the position is empty. * Used when estimating the total number of retrieval requests. */ - public int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos) { return -1; } + public int getMaxPossibleRetrievalPositionCountForPos(Long pos) { return -1; } /** @return true if the position was queued, false if not */ - public boolean queuePositionForRetrieval(DhSectionPos genPos) { return false; } + public boolean queuePositionForRetrieval(Long genPos) { return false; } /** does nothing if the given position isn't present in the queue */ - public void removeRetrievalRequestIf(Function removeIf) { } + public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { } public void clearRetrievalQueue() { } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index ca5594c33..37c6ee221 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -34,6 +34,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.Logger; import java.awt.*; @@ -41,7 +42,6 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.function.Function; public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 implements IDebugRenderable { @@ -116,7 +116,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } // TODO only fire after the section has finished generated or once every X seconds - private void fireOnGenPosSuccessListeners(DhSectionPos pos) + private void fireOnGenPosSuccessListeners(long pos) { // fire the event listeners for (IOnWorldGenCompleteListener listener : this.onWorldGenTaskCompleteListeners) @@ -205,7 +205,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } @Override - public boolean queuePositionForRetrieval(DhSectionPos genPos) + public boolean queuePositionForRetrieval(Long genPos) { IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue == null) @@ -214,14 +214,14 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } GenTask genTask = new GenTask(genPos); - CompletableFuture worldGenFuture = worldGenQueue.submitGenTask(genPos, (byte) (genPos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask); + CompletableFuture worldGenFuture = worldGenQueue.submitGenTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask); worldGenFuture.whenComplete((genTaskResult, ex) -> this.onWorldGenTaskComplete(genTaskResult, ex)); return true; } @Override - public void removeRetrievalRequestIf(Function removeIf) + public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue != null) @@ -238,7 +238,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im @Override - public ArrayList getPositionsToRetrieve(DhSectionPos pos) + public LongArrayList getPositionsToRetrieve(Long pos) { IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue == null) @@ -268,7 +268,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im if (positionFullyGenerated) { - return new ArrayList<>(); + return new LongArrayList(); } } } @@ -277,9 +277,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im // this section is missing one or more columns, queue the missing ones for generation. // TODO speed up this logic by only checking ungenerated columns - ArrayList generationList = new ArrayList<>(); + LongArrayList generationList = new LongArrayList(); byte minGeneratorSectionDetailLevel = (byte) (worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); - pos.forEachChildAtDetailLevel(minGeneratorSectionDetailLevel, (genPos) -> + DhSectionPos.forEachChildAtDetailLevel(pos, minGeneratorSectionDetailLevel, (genPos) -> { if (!this.repo.existsWithKey(genPos)) { @@ -336,7 +336,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } @Override - public int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos) + public int getMaxPossibleRetrievalPositionCountForPos(Long pos) { IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue == null) @@ -345,7 +345,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im } int minGeneratorSectionDetailLevel = worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL; - int detailLevelDiff = pos.getDetailLevel() - minGeneratorSectionDetailLevel; + int detailLevelDiff = DhSectionPos.getDetailLevel(pos) - minGeneratorSectionDetailLevel; return BitShiftUtil.powerOfTwo(detailLevelDiff); } @@ -375,12 +375,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im // TODO may not be needed private class GenTask implements IWorldGenTaskTracker { - private final DhSectionPos pos; + private final long pos; - public GenTask(DhSectionPos pos) - { - this.pos = pos; - } + public GenTask(long pos) { this.pos = pos; } @@ -406,7 +403,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im public interface IOnWorldGenCompleteListener { /** Fired whenever a section has completed generating */ - void onWorldGenTaskComplete(DhSectionPos pos); + void onWorldGenTaskComplete(long pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index a808e5e8b..6089fb823 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -190,7 +190,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable } FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(new DhSectionPos(this.playerData.playerBlockPos)); + FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encode(this.playerData.playerBlockPos)); newDataSource.update(newChunkSizedFullDataView); @@ -215,7 +215,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable // get the data source to compare against try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false)) { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(new DhSectionPos(this.playerData.playerBlockPos)).join(); + testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encode(this.playerData.playerBlockPos)).join(); if (testFullDataSource == null) { continue; @@ -224,9 +224,9 @@ public class SubDimensionLevelMatcher implements AutoCloseable // confirm both data sources have the same section pos - DhSectionPos newSectionChunkPos = newDataSource.getPos().convertNewToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - DhSectionPos testSectionChunkPos = testFullDataSource.getPos().convertNewToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - LodUtil.assertTrue(newSectionChunkPos.equals(testSectionChunkPos), "data source positions don't match"); + long newSectionChunkPos = DhSectionPos.convertToDetailLevel(newDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); + long testSectionChunkPos = DhSectionPos.convertToDetailLevel(testFullDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); + LodUtil.assertTrue(newSectionChunkPos == testSectionChunkPos, "data source positions don't match"); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java index ade0d524e..9a398d25f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.render.LodQuadTree; import java.io.Closeable; import java.util.concurrent.CompletableFuture; -import java.util.function.Function; /** * Used to track what full data sources the system currently @@ -80,9 +79,9 @@ public interface IFullDataSourceRetrievalQueue extends Closeable * Generally the retrieval queue should be fairly small, so its faster to iterate over the existing list * and check if each one is valid vs dumbly attempting to remove every position that just went out of range. */ - void removeRetrievalRequestIf(Function removeIf); + void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf); - CompletableFuture submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); + CompletableFuture submitGenTask(long pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index da1595893..95b712948 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -42,13 +42,13 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; import org.apache.logging.log4j.Logger; import java.awt.*; import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; -import java.util.function.Function; public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDebugRenderable { @@ -58,9 +58,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb private final IDhApiWorldGenerator generator; /** contains the positions that need to be generated */ - private final ConcurrentHashMap waitingTasks = new ConcurrentHashMap<>(); + private final ConcurrentHashMap waitingTasks = new ConcurrentHashMap<>(); - private final ConcurrentHashMap inProgressGenTasksByLodPos = new ConcurrentHashMap<>(); + private final ConcurrentHashMap inProgressGenTasksByLodPos = new ConcurrentHashMap<>(); // granularity is the detail level for batching world generator requests together public final byte maxGranularity; @@ -89,8 +89,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // debug variables to test for duplicate world generator requests // /** limits how many of the previous world gen requests we should track */ private static final int MAX_ALREADY_GENERATED_COUNT = 100; - private final HashMap alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT); - private final Queue alreadyGeneratedPosQueue = new LinkedList<>(); + private final HashMap alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT); + private final LongArrayFIFOQueue alreadyGeneratedPosQueue = new LongArrayFIFOQueue(); /** just used for rendering to the F3 menu */ private int estimatedTotalTaskCount = 0; @@ -131,7 +131,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb //=================// @Override - public CompletableFuture submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + public CompletableFuture submitGenTask(long pos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { // the generator is shutting down, don't add new tasks if (this.generatorClosingFuture != null) @@ -151,7 +151,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb } // Assert that the data at least can fill in 1 single ChunkSizedFullDataAccessor - LodUtil.assertTrue(pos.getDetailLevel() > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); + LodUtil.assertTrue(DhSectionPos.getDetailLevel(pos) > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); CompletableFuture future = new CompletableFuture<>(); @@ -160,11 +160,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb } @Override - public void removeRetrievalRequestIf(Function removeIf) + public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { this.waitingTasks.forEachKey(100, (genPos) -> { - if (removeIf.apply(genPos)) + if (removeIf.accept(genPos)) { this.waitingTasks.remove(genPos); } @@ -254,7 +254,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb Mapper closestTaskMap = this.waitingTasks.reduceEntries(1024, - entry -> new Mapper(entry.getValue(), entry.getValue().pos.getSectionBBoxPos().getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), + entry -> new Mapper(entry.getValue(), DhSectionPos.getSectionBBoxPos(entry.getValue().pos).getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), (aMapper, bMapper) -> aMapper.dist < bMapper.dist ? aMapper : bMapper); if (closestTaskMap == null) @@ -307,14 +307,14 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // split up the task and add each one to the tree LinkedList> childFutures = new LinkedList<>(); - DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.getDetailLevel(), closestTask.pos.getX(), closestTask.pos.getZ()); + long sectionPos = closestTask.pos; WorldGenTask finalClosestTask = closestTask; - sectionPos.forEachChild((childDhSectionPos) -> + DhSectionPos.forEachChild(sectionPos, (childDhSectionPos) -> { CompletableFuture newFuture = new CompletableFuture<>(); childFutures.add(newFuture); - WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.getDetailLevel(), finalClosestTask.taskTracker, newFuture); + WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, DhSectionPos.getDetailLevel(childDhSectionPos), finalClosestTask.taskTracker, newFuture); this.waitingTasks.put(newGenTask.pos, newGenTask); }); @@ -329,12 +329,12 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb private boolean tryStartingWorldGenTaskGroup(InProgressWorldGenTaskGroup newTaskGroup) { byte taskDetailLevel = newTaskGroup.group.dataDetail; - DhSectionPos taskPos = newTaskGroup.group.pos; - byte granularity = (byte) (taskPos.getDetailLevel() - taskDetailLevel); + long taskPos = newTaskGroup.group.pos; + byte granularity = (byte) (DhSectionPos.getDetailLevel(taskPos) - taskDetailLevel); LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity); LodUtil.assertTrue(taskDetailLevel >= this.highestDataDetail && taskDetailLevel <= this.lowestDataDetail); - DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getSectionBBoxPos().getCornerBlockPos()); + DhChunkPos chunkPosMin = new DhChunkPos(DhSectionPos.getSectionBBoxPos(taskPos).getCornerBlockPos()); // check if this is a duplicate generation task if (this.alreadyGeneratedPosHashSet.containsKey(newTaskGroup.group.pos)) @@ -343,16 +343,16 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb //LOGGER.trace("Duplicate generation section " + taskPos + " with granularity [" + granularity + "] at " + chunkPosMin + ". Skipping..."); // sending a success result is necessary to make sure the render sections are reloaded correctly - newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); + newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos))))); return false; } this.alreadyGeneratedPosHashSet.put(newTaskGroup.group.pos, Thread.currentThread().getStackTrace()); - this.alreadyGeneratedPosQueue.add(newTaskGroup.group.pos); + this.alreadyGeneratedPosQueue.enqueue(newTaskGroup.group.pos); // remove extra tracked duplicate positions while (this.alreadyGeneratedPosQueue.size() > MAX_ALREADY_GENERATED_COUNT) { - DhSectionPos posToRemove = this.alreadyGeneratedPosQueue.poll(); + long posToRemove = this.alreadyGeneratedPosQueue.dequeueLong(); this.alreadyGeneratedPosHashSet.remove(posToRemove); } @@ -380,8 +380,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb } else { - //LOGGER.info("Section generation at "+pos+" completed"); - newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); + newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos))))); } boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, newTaskGroup); LodUtil.assertTrue(worked); @@ -624,9 +623,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // helper methods // //================// - private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , DhSectionPos taskPos) + private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , long taskPos) { - byte granularity = (byte) (taskPos.getDetailLevel() - worldGenTaskGroupDetailLevel); + byte granularity = (byte) (DhSectionPos.getDetailLevel(taskPos) - worldGenTaskGroupDetailLevel); return (granularity >= this.minGranularity && granularity <= this.maxGranularity); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenResult.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenResult.java index 27ffd37ef..6aa3e9298 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenResult.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenResult.java @@ -19,8 +19,6 @@ package com.seibel.distanthorizons.core.generation.tasks; -import com.seibel.distanthorizons.core.pos.DhSectionPos; - import java.util.Collection; import java.util.LinkedList; import java.util.concurrent.CompletableFuture; @@ -30,15 +28,15 @@ public class WorldGenResult /** true if terrain was generated */ public final boolean success; /** the position that was generated, will be null if nothing was generated */ - public final DhSectionPos pos; + public final long pos; /** if a position is too high detail for world generator to handle it, these futures are for its 4 children positions after being split up. */ public final LinkedList> childFutures = new LinkedList<>(); - public static WorldGenResult CreateSplit(Collection> siblingFutures) { return new WorldGenResult(false, null, siblingFutures); } - public static WorldGenResult CreateFail() { return new WorldGenResult(false, null, null); } - public static WorldGenResult CreateSuccess(DhSectionPos pos) { return new WorldGenResult(true, pos, null); } - private WorldGenResult(boolean success, DhSectionPos pos, Collection> childFutures) + public static WorldGenResult CreateSplit(Collection> siblingFutures) { return new WorldGenResult(false, 0, siblingFutures); } + public static WorldGenResult CreateFail() { return new WorldGenResult(false, 0, null); } + public static WorldGenResult CreateSuccess(long pos) { return new WorldGenResult(true, pos, null); } + private WorldGenResult(boolean success, long pos, Collection> childFutures) { this.success = success; this.pos = pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java index c9f52c617..123e36167 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java @@ -19,9 +19,6 @@ package com.seibel.distanthorizons.core.generation.tasks; -import com.seibel.distanthorizons.core.pos.DhLodPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; - import java.util.concurrent.CompletableFuture; /** @@ -30,14 +27,14 @@ import java.util.concurrent.CompletableFuture; */ public final class WorldGenTask { - public final DhSectionPos pos; + public final long pos; public final byte dataDetailLevel; public final IWorldGenTaskTracker taskTracker; public final CompletableFuture future; - public WorldGenTask(DhSectionPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture future) + public WorldGenTask(long pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture future) { this.dataDetailLevel = dataDetail; this.pos = pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java index ea7b5b91e..c8130b330 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.generation.tasks; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import java.util.Iterator; import java.util.LinkedList; @@ -33,14 +32,14 @@ import java.util.function.Consumer; @Deprecated // TODO look into how these are used and if they should continue to be used public final class WorldGenTaskGroup { - public final DhSectionPos pos; + public final long pos; public byte dataDetail; /** Only accessed by the generator polling thread */ public final LinkedList worldGenTasks = new LinkedList<>(); - public WorldGenTaskGroup(DhSectionPos pos, byte dataDetail) + public WorldGenTaskGroup(long pos, byte dataDetail) { this.pos = pos; this.dataDetail = dataDetail; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index ccaba1934..080130820 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -37,7 +37,7 @@ public abstract class AbstractDhLevel implements IDhLevel protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000); /** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */ - protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>(); + protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index c24341d62..f474c15d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -29,7 +29,6 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.LodQuadTree; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; @@ -313,7 +312,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } } - public void reloadPos(DhSectionPos pos) + public void reloadPos(long pos) { ClientRenderState clientRenderState = this.ClientRenderStateRef.get(); if (clientRenderState != null && clientRenderState.quadtree != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 0527f61c9..eadcc1ece 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -28,7 +28,6 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -199,7 +198,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev } @Override - public void onWorldGenTaskComplete(DhSectionPos pos) + public void onWorldGenTaskComplete(long pos) { DebugRenderer.makeParticle( new DebugRenderer.BoxParticle( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 8b591258a..addb9416e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -23,7 +23,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -105,7 +104,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); } @Override - public void onWorldGenTaskComplete(DhSectionPos pos) + public void onWorldGenTaskComplete(long pos) { //TODO: Send packet to client } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java index 2709fc6de..7678ec3e8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhLodPos.java @@ -53,7 +53,7 @@ public class DhLodPos implements Comparable this.x = x; this.z = z; } - public DhLodPos(DhSectionPos sectionPos) { this(sectionPos.getDetailLevel(), sectionPos.getX(), sectionPos.getZ()); } + public DhLodPos(long sectionPos) { this(DhSectionPos.getDetailLevel(sectionPos), DhSectionPos.getX(sectionPos), DhSectionPos.getZ(sectionPos)); } @@ -165,7 +165,7 @@ public class DhLodPos implements Comparable * @param sectionDetailLevel This is different from the normal LOD Detail level, see {@link DhSectionPos} for more information * @throws IllegalArgumentException if this position's detail level is lower than the output detail level */ - public DhSectionPos getSectionPosWithSectionDetailLevel(byte sectionDetailLevel) throws IllegalArgumentException + public long getSectionPosWithSectionDetailLevel(byte sectionDetailLevel) throws IllegalArgumentException { if (sectionDetailLevel < this.detailLevel) { @@ -174,7 +174,7 @@ public class DhLodPos implements Comparable DhLodPos lodPos = new DhLodPos(this.detailLevel, this.x, this.z); lodPos = lodPos.convertToDetailLevel(sectionDetailLevel); - return new DhSectionPos(lodPos.detailLevel, lodPos.x, lodPos.z); + return DhSectionPos.encode(lodPos.detailLevel, lodPos.x, lodPos.z); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index 8be858116..c37fac961 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -20,12 +20,10 @@ package com.seibel.distanthorizons.core.pos; import com.seibel.distanthorizons.core.enums.EDhDirection; -import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.util.LodUtil; -import org.jetbrains.annotations.Nullable; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.function.LongConsumer; /** * The position object used to define LOD objects in the quad trees.

@@ -49,19 +47,29 @@ public class DhSectionPos * The lowest detail level a Section position can hold. * This section DetailLevel holds 64 x 64 Block level (detail level 0) LODs. */ - public final static byte SECTION_MINIMUM_DETAIL_LEVEL = 6; + public static final byte SECTION_MINIMUM_DETAIL_LEVEL = 6; - public final static byte SECTION_BLOCK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.BLOCK_DETAIL_LEVEL; - public final static byte SECTION_CHUNK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.CHUNK_DETAIL_LEVEL; - public final static byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL; + public static final byte SECTION_BLOCK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.BLOCK_DETAIL_LEVEL; + public static final byte SECTION_CHUNK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.CHUNK_DETAIL_LEVEL; + public static final byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL; - protected byte detailLevel; - /** in a sectionDetailLevel grid */ - protected int x; - /** in a sectionDetailLevel grid */ - protected int z; + public static final int DETAIL_LEVEL_WIDTH = 8; + public static final int X_POS_WIDTH = 28; + public static final int Z_POS_WIDTH = 28; + public static final int X_POS_MISSING_WIDTH = 32 - 28; + public static final int Z_POS_MISSING_WIDTH = 32 - 28; + + + public static final int DETAIL_LEVEL_OFFSET = 0; + public static final int POS_X_OFFSET = DETAIL_LEVEL_OFFSET + DETAIL_LEVEL_WIDTH; + /** indicates the Y position where the LOD starts relative to the level's minimum height */ + public static final int POS_Z_OFFSET = POS_X_OFFSET + X_POS_WIDTH; + + public static final long DETAIL_LEVEL_MASK = Byte.MAX_VALUE; + public static final int POS_X_MASK = (int) Math.pow(2, X_POS_WIDTH) - 1; + public static final int POS_Z_MASK = (int) Math.pow(2, Z_POS_WIDTH) - 1; @@ -69,35 +77,45 @@ public class DhSectionPos // constructors // //==============// - public DhSectionPos(byte detailLevel, int x, int z) + /** + * This class just holds utility methods for handling a packed + * {@link DhSectionPos} and shouldn't be constructed.

+ * + * Use one of the {@link DhSectionPos#encode(byte, int, int)} methods instead + */ + private DhSectionPos() { } + + + + /** + * Note: + * no validation is done for whether the detail level is positive + * or if the X/Z positions can be represented by available bits. + */ + public static long encode(byte detailLevel, int x, int z) { - this.detailLevel = detailLevel; - this.x = x; - this.z = z; + long data = 0; + data |= detailLevel & DETAIL_LEVEL_MASK; + data |= (long) (x & POS_X_MASK) << POS_X_OFFSET; + data |= (long) (z & POS_Z_MASK) << POS_Z_OFFSET; + return data; } - public DhSectionPos(DhBlockPos blockPos) + public static long encode(DhBlockPos pos) { return encodeBlockPos(pos.x, pos.z); } + public static long encode(DhBlockPos2D pos) { return encodeBlockPos(pos.x, pos.z); } + public static long encodeBlockPos(int blockX, int blockZ) { - this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z); - this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - } - public DhSectionPos(DhBlockPos2D blockPos) - { - this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z); - this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + long pos = encode(LodUtil.BLOCK_DETAIL_LEVEL, blockX, blockZ); + pos = convertToDetailLevel(pos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + return pos; } - public DhSectionPos(DhChunkPos chunkPos) + public static long encode(DhChunkPos pos) { return encodeChunkPos(pos.x, pos.z); } + public static long encodeChunkPos(int chunkX, int chunkZ) { - this(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z); - this.convertSelfToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - } - - public DhSectionPos(byte detailLevel, DhLodPos dhLodPos) - { - this.detailLevel = detailLevel; - this.x = dhLodPos.x; - this.z = dhLodPos.z; + long pos = encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ); + pos = convertToDetailLevel(pos, DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); + return pos; } @@ -105,36 +123,27 @@ public class DhSectionPos //============// // converters // //============// - - /** - * 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 convertNewToDetailLevel(byte newSectionDetailLevel) - { - DhSectionPos newPos = new DhSectionPos(this.detailLevel, this.x, this.z); - newPos.convertSelfToDetailLevel(newSectionDetailLevel); - - 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) + public static long convertToDetailLevel(long pos, byte newDetailLevel) { + byte detailLevel = getDetailLevel(pos); + int x = getX(pos); + int z = getZ(pos); + // logic originally taken from DhLodPos - if (newDetailLevel >= this.detailLevel) + if (newDetailLevel >= detailLevel) { - this.x = Math.floorDiv(this.x, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel)); - this.z = Math.floorDiv(this.z, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel)); + x = Math.floorDiv(x, BitShiftUtil.powerOfTwo(newDetailLevel - detailLevel)); + z = Math.floorDiv(z, BitShiftUtil.powerOfTwo(newDetailLevel - detailLevel)); } else { - this.x = this.x * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel); - this.z = this.z * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel); + x = x * BitShiftUtil.powerOfTwo(detailLevel - newDetailLevel); + z = z * BitShiftUtil.powerOfTwo(detailLevel - newDetailLevel); } - - this.detailLevel = newDetailLevel; + + return encode(newDetailLevel, x, z); } @@ -143,46 +152,41 @@ public class DhSectionPos // property getters // //==================// - public byte getDetailLevel() { return this.detailLevel; } - - public int getX() { return this.x; } - public int getZ() { return this.z; } + public static byte getDetailLevel(long pos) { return (byte) ((pos >> DETAIL_LEVEL_OFFSET) & DETAIL_LEVEL_MASK); } + public static int getX(long pos) + { + // unpack the position + int x = (int) ((pos >> POS_X_OFFSET) & POS_X_MASK); + // add the missing 2's compliment most-significant bits (if not done negative numbers will parse incorrectly) + x = (x << X_POS_MISSING_WIDTH) >> X_POS_MISSING_WIDTH; + return x; + } + public static int getZ(long pos) + { + int Z = (int) ((pos >> POS_Z_OFFSET) & POS_Z_MASK); + Z = (Z << Z_POS_MISSING_WIDTH) >> Z_POS_MISSING_WIDTH; + return Z; + } //=========// // getters // //=========// - - /** - * @deprecated use DhSectionPos instead - * @return the corner with the smallest X and Z coordinate - */ - @Deprecated - public DhLodPos getMinCornerLodPos() { return this.getMinCornerLodPos((byte) (this.detailLevel - 1)); } - /** - * @deprecated use DhSectionPos instead - * @return the corner with the smallest X and Z coordinate - */ - @Deprecated - public DhLodPos getMinCornerLodPos(byte returnDetailLevel) + + /** @return the block X pos that represents the smallest X coordinate of this section */ + public static int getMinCornerBlockX(long pos) { - 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)); + // detail level 1 (2x2 blocks) is a special case, + // if this isn't done it will return (1,1) instead of (0,0) + int halfBlockWidth = (getDetailLevel(pos) != 1) ? (DhSectionPos.getBlockWidth(pos) / 2) : 0; + return DhSectionPos.getCenterBlockPosX(pos) - halfBlockWidth; } - - public DhSectionPos getMinCornerPos(byte returnDetailLevel) + /** @return the block Z pos that represents the smallest Z coordinate of this section */ + public static int getMinCornerBlockZ(long pos) { - LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail"); - - byte offset = (byte) (this.detailLevel - returnDetailLevel); - return new DhSectionPos(returnDetailLevel, - this.x * BitShiftUtil.powerOfTwo(offset), - this.z * BitShiftUtil.powerOfTwo(offset)); + int halfBlockWidth = (getDetailLevel(pos) != 1) ? (DhSectionPos.getBlockWidth(pos) / 2) : 0; + return DhSectionPos.getCenterBlockPosZ(pos) - halfBlockWidth; } /** @@ -195,26 +199,33 @@ public class DhSectionPos * * @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) + public static int getWidthCountForLowerDetailedSection(long pos, byte returnDetailLevel) { - LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail"); - byte offset = (byte) (this.detailLevel - returnDetailLevel); + byte detailLevel = getDetailLevel(pos); + + LodUtil.assertTrue(returnDetailLevel <= detailLevel, "returnDetailLevel must be less than sectionDetail"); + byte offset = (byte) (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) + public static int getBlockWidth(long pos) { return BitShiftUtil.powerOfTwo(getDetailLevel(pos)); } + + + public static DhBlockPos2D getCenterBlockPos(long pos) { return new DhBlockPos2D(getCenterBlockPosX(pos), getCenterBlockPosZ(pos)); } + + public static int getCenterBlockPosX(long pos) { return getCenterBlockPosXOrZ(pos, true); } + public static int getCenterBlockPosZ(long pos) { return getCenterBlockPosXOrZ(pos, false); } + private static int getCenterBlockPosXOrZ(long pos, boolean returnX) { - int centerBlockPos = returnX ? this.x : this.z; + byte detailLevel = getDetailLevel(pos); + int x = getX(pos); + int z = getZ(pos); - if (this.detailLevel == 0) + + int centerBlockPos = returnX ? x : z; + + if (detailLevel == 0) { // already at block detail level, no conversion necessary return centerBlockPos; @@ -222,18 +233,18 @@ public class DhSectionPos // 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) + if (detailLevel != 1) { - positionOffset = BitShiftUtil.powerOfTwo(this.detailLevel - 1); + positionOffset = BitShiftUtil.powerOfTwo(detailLevel - 1); } - return (centerBlockPos * BitShiftUtil.powerOfTwo(this.detailLevel)) + positionOffset; + return (centerBlockPos * BitShiftUtil.powerOfTwo(detailLevel)) + positionOffset; } - public int getManhattanBlockDistance(DhBlockPos2D blockPos) + public static int getManhattanBlockDistance(long pos, DhBlockPos2D blockPos) { - return Math.abs(this.getCenterBlockPosX() - blockPos.x) - + Math.abs(this.getCenterBlockPosZ() - blockPos.z); + return Math.abs(getCenterBlockPosX(pos) - blockPos.x) + + Math.abs(getCenterBlockPosZ(pos) - blockPos.z); } @@ -241,9 +252,9 @@ public class DhSectionPos //==================// // parent child pos // //==================// - + /** - * Returns the DhLodPos 1 detail level lower

+ * Returns a position 1 detail level lower.

* * Relative child positions returned for each index:
* 0 = (0,0) - North West
@@ -253,248 +264,147 @@ public class DhSectionPos * * @param child0to3 must be an int between 0 and 3 */ - public DhSectionPos getChildByIndex(int child0to3) throws IllegalArgumentException, IllegalStateException + public static long getChildByIndex(long pos, int child0to3) throws IllegalArgumentException, IllegalStateException { + byte detailLevel = getDetailLevel(pos); + int x = getX(pos); + int z = getZ(pos); + if (child0to3 < 0 || child0to3 > 3) { throw new IllegalArgumentException("child0to3 must be between 0 and 3"); } - if (this.detailLevel <= 0) + if (detailLevel <= 0) { throw new IllegalStateException("section detail must be greater than 0"); } - - return new DhSectionPos((byte) (this.detailLevel - 1), - this.x * 2 + (child0to3 & 1), - this.z * 2 + BitShiftUtil.half(child0to3 & 2)); + + return DhSectionPos.encode((byte) (detailLevel - 1), + x * 2 + (child0to3 & 1), + z * 2 + BitShiftUtil.half(child0to3 & 2)); } /** Returns this position's child index in its parent */ - public int getChildIndexOfParent() { return (this.x & 1) + BitShiftUtil.square(this.z & 1); } + public static int getChildIndexOfParent(long pos) { return (getX(pos) & 1) + BitShiftUtil.square(getZ(pos) & 1); } - public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.detailLevel + 1), BitShiftUtil.half(this.x), BitShiftUtil.half(this.z)); } + public static long getParentPos(long pos) { return DhSectionPos.encode((byte) (getDetailLevel(pos) + 1), BitShiftUtil.half(getX(pos)), BitShiftUtil.half(getZ(pos))); } - - - - public DhSectionPos getAdjacentPos(EDhDirection dir) + + + public static long getAdjacentPos(long pos, EDhDirection dir) throws IllegalArgumentException { - return new DhSectionPos(this.detailLevel, - this.x + dir.getNormal().x, - this.z + dir.getNormal().z); + if (dir == EDhDirection.UP || dir == EDhDirection.DOWN) + { + throw new IllegalArgumentException("getAdjacentPos can't be UP or DOWN, direction given: ["+dir.name()+"]."); + } + + return DhSectionPos.encode(getDetailLevel(pos), + getX(pos) + dir.getNormal().x, + getZ(pos) + dir.getNormal().z); } - public DhLodPos getSectionBBoxPos() { return new DhLodPos(this.detailLevel, this.x, this.z); } - - - + @Deprecated + public static DhLodPos getSectionBBoxPos(long pos) { return new DhLodPos(getDetailLevel(pos), getX(pos), getZ(pos)); } + + + //=============// // comparisons // //=============// - public boolean overlapsExactly(DhSectionPos other) + public static boolean contains(long aPos, long bPos) { - // 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 aMinX = getMinCornerBlockX(aPos); + int aMinZ = getMinCornerBlockZ(aPos); - 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; + int bMinX = getMinCornerBlockX(bPos); + int bMinZ = getMinCornerBlockZ(bPos); + + int aBlockWidth = getBlockWidth(aPos) - 1; // minus 1 to account for zero based positional indexing + int aMaxX = aMinX + aBlockWidth; + int aMaxZ = aMinZ + aBlockWidth; + + return aMinX <= bMinX && bMinX <= aMaxX && + aMinZ <= bMinZ && bMinZ <= aMaxZ; } - - - + + + //===========// // iterators // //===========// - + /** Applies the given consumer to all 4 of this position's children. */ - public void forEachChild(Consumer callback) + public static void forEachChild(long pos, LongConsumer callback) throws IllegalArgumentException, IllegalStateException { for (int i = 0; i < 4; i++) { - callback.accept(this.getChildByIndex(i)); + callback.accept(getChildByIndex(pos, i)); } } - + /** Applies the given consumer to all children of the position at the given section detail level. */ - public void forEachChildDownToDetailLevel(byte minSectionDetailLevel, Function callback) + public static void forEachChildDownToDetailLevel(long pos, byte minSectionDetailLevel, ICancelablePrimitiveLongConsumer callback) throws IllegalArgumentException, IllegalStateException { - boolean stop = callback.apply(this); - if (stop || minSectionDetailLevel == this.detailLevel) + boolean stop = callback.accept(pos); + if (stop || minSectionDetailLevel == getDetailLevel(pos)) { return; } for (int i = 0; i < 4; i++) { - this.getChildByIndex(i).forEachChildDownToDetailLevel(minSectionDetailLevel, callback); + forEachChildDownToDetailLevel(getChildByIndex(pos, i), minSectionDetailLevel, callback); } } - + /** Applies the given consumer to all children of the position at the given section detail level. */ - public void forEachChildAtDetailLevel(byte sectionDetailLevel, Consumer callback) + public static void forEachChildAtDetailLevel(long pos, byte sectionDetailLevel, LongConsumer callback) throws IllegalArgumentException, IllegalStateException { - if (sectionDetailLevel == this.detailLevel) + if (sectionDetailLevel == getDetailLevel(pos)) { - callback.accept(this); + callback.accept(pos); return; } - + for (int i = 0; i < 4; i++) { - this.getChildByIndex(i).forEachChildAtDetailLevel(sectionDetailLevel, callback); + forEachChildAtDetailLevel(getChildByIndex(pos, i), sectionDetailLevel, callback); } } - + /** Applies the given consumer to all children of the position at the given section detail level. */ - public void forEachPosUpToDetailLevel(byte maxSectionDetailLevel, Consumer callback) + public static void forEachPosUpToDetailLevel(long pos, byte maxSectionDetailLevel, LongConsumer callback) { - callback.accept(this); - if (maxSectionDetailLevel == this.detailLevel) + callback.accept(pos); + if (maxSectionDetailLevel == getDetailLevel(pos)) { return; } - this.getParentPos().forEachPosUpToDetailLevel(maxSectionDetailLevel, callback); + forEachPosUpToDetailLevel(getParentPos(pos), maxSectionDetailLevel, callback); } + //==============// + // Base methods // + //==============// + + public static String toString(long pos) { return getDetailLevel(pos) + "*" + getX(pos) + "," + getZ(pos); } + public static int hashCode(long pos) { return Long.hashCode(pos); } - //===============// - // serialization // - //===============// - /** Serialize() is different from toString() as it must NEVER be changed, and should be in a short format */ - public String serialize() { return "[" + this.detailLevel + ',' + this.x + ',' + this.z + ']'; } + //================// + // helper methods // + //================// - @Nullable - public static DhSectionPos deserialize(String value) + /** Used instead of {@link java.util.function.Function} to prevent unnecessary (un)wrapping. */ + @FunctionalInterface + public interface ICancelablePrimitiveLongConsumer { - if (value.charAt(0) != '[' || value.charAt(value.length() - 1) != ']') return null; - String[] split = value.substring(1, value.length() - 1).split(","); - if (split.length != 3) return null; - return new DhSectionPos(Byte.parseByte(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); - - } - - - - //===========// - // overrides // - //===========// - - @Override - public String toString() { return "{" + this.detailLevel + "*" + this.x + "," + this.z + "}"; } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null || obj.getClass() != DhSectionPos.class) - { - return false; - } - - DhSectionPos that = (DhSectionPos) obj; - return this.detailLevel == that.detailLevel && - this.x == that.x && - this.z == that.z; - } - - @Override - public int hashCode() - { - return Integer.hashCode(this.detailLevel) ^ // XOR - Integer.hashCode(this.x) ^ // XOR - Integer.hashCode(this.z); - } - - - - //=============// - // sub classes // - //=============// - - /** - * Identical to {@link DhSectionPos} except it is mutable. - * See {@link DhSectionPos} for full documentation. - * - * @see DhSectionPos - */ - public static class DhMutableSectionPos extends DhSectionPos - { - - //==============// - // constructors // - //==============// - - public DhMutableSectionPos(byte sectionDetailLevel, int sectionX, int sectionZ) { super(sectionDetailLevel, sectionX, sectionZ); } - public DhMutableSectionPos(DhBlockPos blockPos) { super(blockPos); } - public DhMutableSectionPos(DhBlockPos2D blockPos) { super(blockPos); } - public DhMutableSectionPos(DhChunkPos chunkPos) { super(chunkPos); } - public DhMutableSectionPos(byte detailLevel, DhLodPos dhLodPos) { super(detailLevel, dhLodPos); } - - - - //============// - // converters // - //============// - - /** - * Overwrites this section pos with the given input.
- * Can be useful to prevent duplicate allocations in high traffic loops but should - * be used sparingly as it could accidentally cause bugs due to unexpected modifications. - */ - public void mutate(byte sectionDetailLevel, int sectionX, int sectionZ) - { - this.detailLevel = sectionDetailLevel; - this.x = sectionX; - this.z = sectionZ; - } - - @Override - public void convertSelfToDetailLevel(byte newDetailLevel) { super.convertSelfToDetailLevel(newDetailLevel); } - - - - //==================// - // property getters // - //==================// - - public void setDetailLevel(byte sectionDetailLevel) { this.detailLevel = sectionDetailLevel; } - - public void setX(int sectionX) { this.x = sectionX; } - - public void setZ(int sectionZ) { this.z = sectionZ; } - + /** @return true if this method should cancel further consumers. */ + boolean accept(long value); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 681aba2c1..731cba4d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -36,8 +36,8 @@ import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree; import com.seibel.distanthorizons.coreapi.util.MathUtil; +import it.unimi.dsi.fastutil.longs.LongIterator; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import javax.annotation.WillNotClose; import java.awt.*; @@ -71,7 +71,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen * This holds every {@link DhSectionPos} that should be reloaded next tick.
* This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads. */ - private final ConcurrentLinkedQueue sectionsToReload = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue sectionsToReload = new ConcurrentLinkedQueue<>(); private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference! private final ConfigChangeListener horizontalScaleChangeListener; private final ReentrantLock treeReadWriteLock = new ReentrantLock(); @@ -181,12 +181,12 @@ public class LodQuadTree extends QuadTree implements IDebugRen // reload any sections that need it - DhSectionPos pos; + Long pos; while ((pos = this.sectionsToReload.poll()) != null) { // 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.getDetailLevel() <= this.treeMinDetailLevel) + while (DhSectionPos.getDetailLevel(pos) <= this.treeMinDetailLevel) { try { @@ -199,7 +199,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen catch (IndexOutOfBoundsException e) { /* the section is now out of bounds, it doesn't need to be reloaded */ } - pos = pos.getParentPos(); + pos = DhSectionPos.getParentPos(pos); } } @@ -207,11 +207,11 @@ public class LodQuadTree extends QuadTree implements IDebugRen // walk through each root node ArrayList nodesNeedingRetrieval = new ArrayList<>(); ArrayList nodesNeedingLoading = new ArrayList<>(); - Iterator rootPosIterator = this.rootNodePosIterator(); + LongIterator rootPosIterator = this.rootNodePosIterator(); while (rootPosIterator.hasNext()) { // make sure all root nodes have been created - DhSectionPos rootPos = rootPosIterator.next(); + long rootPos = rootPosIterator.nextLong(); if (this.getNode(rootPos) == null) { this.setValue(rootPos, new LodRenderSection(rootPos, this, this.level, this.fullDataSourceProvider)); @@ -232,8 +232,8 @@ public class LodQuadTree extends QuadTree implements IDebugRen nodesNeedingLoading.sort((a, b) -> { - int aDist = a.pos.getManhattanBlockDistance(playerPos); - int bDist = b.pos.getManhattanBlockDistance(playerPos); + int aDist = DhSectionPos.getManhattanBlockDistance(a.pos, playerPos); + int bDist = DhSectionPos.getManhattanBlockDistance(b.pos, playerPos); return Integer.compare(aDist, bDist); }); @@ -250,7 +250,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen /** @return whether the current position is able to render (note: not if it IS rendering, just if it is ABLE to.) */ private boolean recursivelyUpdateRenderSectionNode( DhBlockPos2D playerPos, - QuadNode rootNode, QuadNode quadNode, DhSectionPos sectionPos, + QuadNode rootNode, QuadNode quadNode, long sectionPos, boolean parentSectionIsRendering, ArrayList nodesNeedingRetrieval, ArrayList nodesNeedingLoading) @@ -295,17 +295,17 @@ public class LodQuadTree extends QuadTree implements IDebugRen expectedDetailLevel += DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL; - if (sectionPos.getDetailLevel() > expectedDetailLevel) + if (DhSectionPos.getDetailLevel(sectionPos) > expectedDetailLevel) { // section detail level too high // boolean thisPosIsRendering = renderSection.renderingEnabled; boolean allChildrenSectionsAreLoaded = true; // recursively update all child render sections - Iterator childPosIterator = quadNode.getChildPosIterator(); + LongIterator childPosIterator = quadNode.getChildPosIterator(); while (childPosIterator.hasNext()) { - DhSectionPos childPos = childPosIterator.next(); + long childPos = childPosIterator.nextLong(); QuadNode childNode = rootNode.getNode(childPos); boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, thisPosIsRendering || parentSectionIsRendering, nodesNeedingRetrieval, nodesNeedingLoading); @@ -338,7 +338,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen childPosIterator = quadNode.getChildPosIterator(); while (childPosIterator.hasNext()) { - DhSectionPos childPos = childPosIterator.next(); + long childPos = childPosIterator.nextLong(); QuadNode childNode = rootNode.getNode(childPos); boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, parentSectionIsRendering, nodesNeedingRetrieval, nodesNeedingLoading); @@ -348,7 +348,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen { // FIXME having world generation enabled in a pre-generated world that doesn't have any DH data can cause this to happen // surprisingly reloadPos() doesn't appear to be the culprit, maybe there is an issue with reloading/changing the full data source? - //LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+sectionPos); + //LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+DhSectionPos.toString(sectionPos)); } // this section is now being rendered via its children @@ -356,7 +356,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } } // 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.getDetailLevel() == expectedDetailLevel || sectionPos.getDetailLevel() == expectedDetailLevel - 1) + else if (DhSectionPos.getDetailLevel(sectionPos) == expectedDetailLevel || DhSectionPos.getDetailLevel(sectionPos) == expectedDetailLevel - 1) { // this is the detail level we want to render // @@ -445,7 +445,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen * @param sectionPos section position * @return detail level of this section pos */ - public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenterBlockPosX(), sectionPos.getCenterBlockPosZ())); } + public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos))); } private byte getDetailLevelFromDistance(double distance) { double maxDetailDistance = this.getDrawDistanceFromDetail(Byte.MAX_VALUE - 1); @@ -537,16 +537,8 @@ public class LodQuadTree extends QuadTree implements IDebugRen * Can be called whenever a render section's data needs to be refreshed.
* This should be called whenever a world generation task is completed or if the connected server has new data to show. */ - public void reloadPos(@NotNull DhSectionPos pos) + public void reloadPos(long pos) { - if (pos == null) - { - // shouldn't happen, but James saw it happen once, so this is here just in case - LOGGER.warn("reloadPos given a null pos."); - return; - } - - this.sectionsToReload.add(pos); // the adjacent locations also need to be updated to make sure lighting @@ -554,7 +546,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen // and lights may not show up over LOD borders for (EDhDirection direction : EDhDirection.ADJ_DIRECTIONS) { - this.sectionsToReload.add(pos.getAdjacentPos(direction)); + this.sectionsToReload.add(DhSectionPos.getAdjacentPos(pos, direction)); } } @@ -574,8 +566,8 @@ public class LodQuadTree extends QuadTree implements IDebugRen // sort the nodes from nearest to farthest nodesNeedingRetrieval.sort((a, b) -> { - int aDist = a.pos.getManhattanBlockDistance(playerPos); - int bDist = b.pos.getManhattanBlockDistance(playerPos); + int aDist = DhSectionPos.getManhattanBlockDistance(a.pos, playerPos); + int bDist = DhSectionPos.getManhattanBlockDistance(b.pos, playerPos); return Integer.compare(aDist, bDist); }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index 2a9210c74..fbfd9a2ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -34,6 +34,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; +import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; @@ -56,7 +57,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable - public final DhSectionPos pos; + public final long pos; private final IDhClientLevel level; @WillNotClose @@ -85,7 +86,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable private boolean missingPositionsCalculated = false; /** should be an empty array if no positions need to be generated */ - private ArrayList missingGenerationPos = null; + private LongArrayList missingGenerationPos = null; @@ -93,7 +94,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable // constructor // //=============// - public LodRenderSection(DhSectionPos pos, LodQuadTree quadTree, IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider) + public LodRenderSection(long pos, LodQuadTree quadTree, IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider) { this.pos = pos; this.quadTree = quadTree; @@ -236,7 +237,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i]; int arrayIndex = direction.ordinal() - 2; - DhSectionPos adjPos = this.pos.getAdjacentPos(direction); + long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction); try { LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos); @@ -350,7 +351,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable break; } - DhSectionPos pos = this.missingGenerationPos.remove(i); + long pos = this.missingGenerationPos.removeLong(i); boolean positionQueued = this.fullDataSourceProvider.queuePositionForRetrieval(pos); if (!positionQueued) { @@ -443,7 +444,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable try { - executor.execute(() -> this.fullDataSourceProvider.removeRetrievalRequestIf((genPos) -> this.pos.contains(genPos))); + executor.execute(() -> this.fullDataSourceProvider.removeRetrievalRequestIf((genPos) -> DhSectionPos.contains(this.pos, genPos))); } catch (RejectedExecutionException ignore) { /* If this happens that means everything is already shut down and no additional cleanup will be necessary */ } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index e7c8524dc..3c151d7d9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -205,8 +205,8 @@ public class RenderBufferHandler implements AutoCloseable // Now that we have the axis directions, we can sort the render list Comparator farToNearComparator = (loadedBufferA, loadedBufferB) -> { - Pos2D aPos = loadedBufferA.pos.getCenterBlockPos().toPos2D(); - Pos2D bPos = loadedBufferB.pos.getCenterBlockPos().toPos2D(); + Pos2D aPos = DhSectionPos.getCenterBlockPos(loadedBufferA.pos).toPos2D(); + Pos2D bPos = DhSectionPos.getCenterBlockPos(loadedBufferB.pos).toPos2D(); if (true) { int aManhattanDistance = aPos.manhattanDist(cPos); @@ -243,7 +243,7 @@ public class RenderBufferHandler implements AutoCloseable return abPosDifference; } - return loadedBufferA.pos.getDetailLevel() - loadedBufferB.pos.getDetailLevel(); // If all else fails, sort by detail + return DhSectionPos.getDetailLevel(loadedBufferA.pos) - DhSectionPos.getDetailLevel(loadedBufferB.pos); // If all else fails, sort by detail }; this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); // TODO is the comparator named wrong? @@ -309,7 +309,7 @@ public class RenderBufferHandler implements AutoCloseable { QuadNode node = nodeIterator.next(); - DhSectionPos sectionPos = node.sectionPos; + long sectionPos = node.sectionPos; LodRenderSection renderSection = node.value; if (renderSection == null) { @@ -320,7 +320,7 @@ public class RenderBufferHandler implements AutoCloseable { if (enableFrustumCulling) { - DhLodPos lodBounds = renderSection.pos.getSectionBBoxPos(); + DhLodPos lodBounds = DhSectionPos.getSectionBBoxPos(renderSection.pos); int blockMinX = lodBounds.getMinX().toBlockWidth(); int blockMinZ = lodBounds.getMinZ().toBlockWidth(); int lodBlockWidth = lodBounds.getBlockWidth(); @@ -425,9 +425,9 @@ public class RenderBufferHandler implements AutoCloseable private static class LoadedRenderBuffer { public final ColumnRenderBuffer buffer; - public final DhSectionPos pos; + public final long pos; - LoadedRenderBuffer(ColumnRenderBuffer buffer, DhSectionPos pos) + LoadedRenderBuffer(ColumnRenderBuffer buffer, long pos) { this.buffer = buffer; this.pos = pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java index c6fa77bbc..42d893b9d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java @@ -247,6 +247,8 @@ public class DebugRenderer public Vec3f b; public Color color; + + public Box(Vec3f a, Vec3f b, Color color) { this.a = a; @@ -288,14 +290,14 @@ public class DebugRenderer this.color = color; } - public Box(DhSectionPos pos, float minY, float maxY, float marginPercent, Color color) + public Box(long pos, float minY, float maxY, float marginPercent, Color color) { - this(pos.getSectionBBoxPos(), minY, maxY, marginPercent, color); + this(DhSectionPos.getSectionBBoxPos(pos), minY, maxY, marginPercent, color); } - public Box(DhSectionPos pos, float y, float yDiff, Object hash, float marginPercent, Color color) + public Box(long pos, float y, float yDiff, Object hash, float marginPercent, Color color) { - this(pos.getSectionBBoxPos(), y, yDiff, hash, marginPercent, color); + this(DhSectionPos.getSectionBBoxPos(pos), y, yDiff, hash, marginPercent, color); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV1DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV1DTO.java index 6c5d1d7ce..5aaf93546 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV1DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV1DTO.java @@ -22,21 +22,18 @@ package com.seibel.distanthorizons.core.sql.dto; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV1; -import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.atomic.AtomicLong; /** * Handles storing{@link FullDataSourceV1}'s in the database. */ -public class FullDataSourceV1DTO implements IBaseDTO +public class FullDataSourceV1DTO implements IBaseDTO { - public DhSectionPos pos; + public long pos; public int checksum; public byte dataDetailLevel; public EDhApiWorldGenerationStep worldGenStep; @@ -54,7 +51,7 @@ public class FullDataSourceV1DTO implements IBaseDTO // constructor // //=============// - public FullDataSourceV1DTO(DhSectionPos pos, int checksum, byte dataDetailLevel, EDhApiWorldGenerationStep worldGenStep, String dataType, byte binaryDataFormatVersion, byte[] dataArray) + public FullDataSourceV1DTO(long pos, int checksum, byte dataDetailLevel, EDhApiWorldGenerationStep worldGenStep, String dataType, byte binaryDataFormatVersion, byte[] dataArray) { this.pos = pos; this.checksum = checksum; @@ -83,7 +80,7 @@ public class FullDataSourceV1DTO implements IBaseDTO //===========// @Override - public DhSectionPos getKey() { return this.pos; } + public Long getKey() { return this.pos; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 99ba7e88a..62d622fc5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; @@ -39,12 +38,12 @@ import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; /** handles storing {@link FullDataSourceV2}'s in the database. */ -public class FullDataSourceV2DTO implements IBaseDTO +public class FullDataSourceV2DTO implements IBaseDTO { public static final boolean VALIDATE_INPUT_DATAPOINTS = true; - public DhSectionPos pos; + public long pos; public int levelMinY; @@ -91,7 +90,7 @@ public class FullDataSourceV2DTO implements IBaseDTO } public FullDataSourceV2DTO( - DhSectionPos pos, + long pos, int dataChecksum, byte[] compressedColumnGenStepByteArray, byte[] compressedWorldCompressionModeByteArray, byte dataFormatVersion, byte compressionModeValue, byte[] compressedDataByteArray, long lastModifiedUnixDateTime, long createdUnixDateTime, byte[] compressedMappingByteArray, boolean applyToParent, @@ -342,7 +341,7 @@ public class FullDataSourceV2DTO implements IBaseDTO return byteArrayOutputStream.toByteArray(); } - private static FullDataPointIdMap readBlobToDataMapping(byte[] compressedMappingByteArray, DhSectionPos pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException + private static FullDataPointIdMap readBlobToDataMapping(byte[] compressedMappingByteArray, long pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedMappingByteArray); DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum); @@ -358,7 +357,7 @@ public class FullDataSourceV2DTO implements IBaseDTO //===========// @Override - public DhSectionPos getKey() { return this.pos; } + public Long getKey() { return this.pos; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java index 20e5e7014..5b3316748 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.sql.repo; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.sql.DatabaseUpdater; import com.seibel.distanthorizons.core.sql.DbConnectionClosedException; import com.seibel.distanthorizons.core.sql.dto.IBaseDTO; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java index 03a9f232c..0c7c444b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java @@ -23,6 +23,8 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratio import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV1DTO; import com.seibel.distanthorizons.coreapi.util.StringUtil; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import org.jetbrains.annotations.Nullable; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -30,7 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -public class FullDataSourceV1Repo extends AbstractDhRepo +public class FullDataSourceV1Repo extends AbstractDhRepo { public static final String TABLE_NAME = "Legacy_FullData_V1"; @@ -55,7 +57,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo objectMap) throws ClassCastException { String posString = (String) objectMap.get("DhSectionPos"); - DhSectionPos pos = DhSectionPos.deserialize(posString); + Long pos = deserializeSectionPos(posString); // meta data int checksum = (Integer) objectMap.get("Checksum"); @@ -106,7 +108,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo getPositionsToMigrate(int returnCount) + public LongArrayList getPositionsToMigrate(int returnCount) { - ArrayList list = new ArrayList<>(); + LongArrayList list = new LongArrayList(); List> resultMapList = this.queryDictionary( "select DhSectionPos " + @@ -218,19 +220,19 @@ public class FullDataSourceV1Repo extends AbstractDhRepo resultMap : resultMapList) { // returned in the format [sectionDetailLevel,x,z] IE [6,0,0] - DhSectionPos sectionPos = DhSectionPos.deserialize((String) resultMap.get("DhSectionPos")); + long sectionPos = deserializeSectionPos((String) resultMap.get("DhSectionPos")); list.add(sectionPos); } return list; } - public void markMigrationFailed(DhSectionPos pos) + public void markMigrationFailed(long pos) { String sql = "UPDATE "+this.getTableName()+" \n" + "SET MigrationFailed = 1 \n" + - "WHERE DhSectionPos = '"+pos.serialize()+"'"; + "WHERE DhSectionPos = '"+serializeSectionPos(pos)+"'"; this.queryDictionaryFirst(sql); } @@ -283,4 +285,30 @@ public class FullDataSourceV1Repo extends AbstractDhRepo +public class FullDataSourceV2Repo extends AbstractDhRepo { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -61,10 +60,10 @@ public class FullDataSourceV2Repo extends AbstractDhRepo resultMap = this.queryDictionaryFirst( "select ColumnGenerationStep, CompressionMode " + "from "+this.getTableName()+" " + - "WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ()); + "WHERE DetailLevel = "+detailLevel+" AND PosX = "+ DhSectionPos.getX(pos)+" AND PosZ = "+ DhSectionPos.getZ(pos)); if (resultMap != null) { @@ -267,7 +266,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo getAllPositions() + public LongArrayList getAllPositions() { - ArrayList list = new ArrayList<>(); + LongArrayList list = new LongArrayList(); List> resultMapList = this.queryDictionary( "select DetailLevel, PosX, PosZ " + @@ -299,7 +298,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo resultMap = this.queryDictionaryFirst( "select LENGTH(Data) as dataSize " + "from "+this.getTableName()+" " + - "WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ()); + "WHERE DetailLevel = "+detailLevel+" AND PosX = "+ DhSectionPos.getX(pos)+" AND PosZ = "+ DhSectionPos.getZ(pos)); if (resultMap != null && resultMap.get("dataSize") != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java index a8794be82..142251213 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadNode.java @@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadNodeDirectChildIterator; import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadNodeDirectChildPosIterator; import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadTreeNodeIterator; +import it.unimi.dsi.fastutil.longs.LongIterator; import org.apache.logging.log4j.Logger; import java.util.Iterator; @@ -35,7 +36,7 @@ public class QuadNode private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public final DhSectionPos sectionPos; + public final long sectionPos; public final byte minimumDetailLevel; public T value; @@ -67,7 +68,7 @@ public class QuadNode - public QuadNode(DhSectionPos sectionPos, byte minimumDetailLevel) + public QuadNode(long sectionPos, byte minimumDetailLevel) { this.sectionPos = sectionPos; this.minimumDetailLevel = minimumDetailLevel; @@ -145,14 +146,14 @@ public class QuadNode * @return the node at the given position * @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this node */ - public QuadNode getNode(DhSectionPos sectionPos) throws IllegalArgumentException { return this.getOrSetValue(sectionPos, false, null); } + public QuadNode getNode(long sectionPos) throws IllegalArgumentException { return this.getOrSetValue(sectionPos, false, null); } /** * @param sectionPos must be 1 detail level lower than this node's detail level * @return the value at the given position before the new value was set * @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this node */ - public T setValue(DhSectionPos sectionPos, T newValue) throws IllegalArgumentException + public T setValue(long sectionPos, T newValue) throws IllegalArgumentException { QuadNode previousNode = this.getNode(sectionPos); if (previousNode != null) @@ -173,27 +174,27 @@ public class QuadNode * @return the node at the given position before the new node was set (if the new node should be set) * @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this */ - private QuadNode getOrSetValue(DhSectionPos inputSectionPos, boolean replaceValue, T newValue) throws IllegalArgumentException + private QuadNode getOrSetValue(long inputSectionPos, boolean replaceValue, T newValue) throws IllegalArgumentException { // debug validation - if (!this.sectionPos.contains(inputSectionPos)) + if (!DhSectionPos.contains(this.sectionPos, inputSectionPos)) { - 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()); + LOGGER.error((replaceValue ? "set " : "get ") + inputSectionPos + " center block: " + DhSectionPos.getCenterBlockPos(inputSectionPos) + ", this pos: " + this.sectionPos + " this center block: " + DhSectionPos.getCenterBlockPos(this.sectionPos)); + throw new IllegalArgumentException("Input section pos " + inputSectionPos + " outside of this quadNode's pos: " + this.sectionPos + ", this node's blockPos: " + DhSectionPos.convertToDetailLevel(this.sectionPos, LodUtil.BLOCK_DETAIL_LEVEL) + " block width: " + DhSectionPos.getBlockWidth(this.sectionPos) + " input detail level: " + DhSectionPos.convertToDetailLevel(inputSectionPos, LodUtil.BLOCK_DETAIL_LEVEL) + " width: " + DhSectionPos.getBlockWidth(inputSectionPos)); } - if (inputSectionPos.getDetailLevel() > this.sectionPos.getDetailLevel()) + if (DhSectionPos.getDetailLevel(inputSectionPos) > DhSectionPos.getDetailLevel(this.sectionPos)) { - throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + this.sectionPos.getDetailLevel() + " input detail level: " + inputSectionPos.getDetailLevel()); + throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + DhSectionPos.getDetailLevel(this.sectionPos) + " input detail level: " + DhSectionPos.getDetailLevel(inputSectionPos)); } - if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel() && !inputSectionPos.equals(this.sectionPos)) + if (DhSectionPos.getDetailLevel(inputSectionPos) == DhSectionPos.getDetailLevel(this.sectionPos) && inputSectionPos != 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.getDetailLevel() < this.minimumDetailLevel) + if (DhSectionPos.getDetailLevel(inputSectionPos) < 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 +202,7 @@ public class QuadNode // get/set logic - if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel()) + if (DhSectionPos.getDetailLevel(inputSectionPos) == DhSectionPos.getDetailLevel(this.sectionPos)) { // this node is the requested position if (replaceValue) @@ -217,14 +218,14 @@ public class QuadNode // LOGGER.info((replaceValue ? "set " : "get ")+inputSectionPos+" center block: "+inputSectionPos.getCenter().getCornerBlockPos()+", this pos: "+this.sectionPos+" this center block: "+this.sectionPos.getCenter().getCornerBlockPos()); - DhSectionPos nwPos = this.sectionPos.getChildByIndex(0); - DhSectionPos swPos = this.sectionPos.getChildByIndex(1); - DhSectionPos nePos = this.sectionPos.getChildByIndex(2); - DhSectionPos sePos = this.sectionPos.getChildByIndex(3); + long nwPos = DhSectionPos.getChildByIndex(this.sectionPos, 0); + long swPos = DhSectionPos.getChildByIndex(this.sectionPos, 1); + long nePos = DhSectionPos.getChildByIndex(this.sectionPos, 2); + long sePos = DhSectionPos.getChildByIndex(this.sectionPos, 3); // look for the child that contains the input position (there may be a faster way to do this, but this works for now) QuadNode childNode; - if (nwPos.contains(inputSectionPos)) + if (DhSectionPos.contains(nwPos, inputSectionPos)) { // TODO merge duplicate code if (replaceValue && this.nwChild == null) @@ -237,7 +238,7 @@ public class QuadNode // childNode should only be null when replaceValue = false and the end of a node chain has been reached return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null; } - else if (swPos.contains(inputSectionPos)) + else if (DhSectionPos.contains(swPos, inputSectionPos)) { // TODO merge duplicate code if (replaceValue && this.swChild == null) @@ -250,7 +251,7 @@ public class QuadNode // childNode should only be null when replaceValue = false and the end of a node chain has been reached return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null; } - else if (nePos.contains(inputSectionPos)) + else if (DhSectionPos.contains(nePos, inputSectionPos)) { // TODO merge duplicate code if (replaceValue && this.neChild == null) @@ -263,7 +264,7 @@ public class QuadNode // childNode should only be null when replaceValue = false and the end of a node chain has been reached return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null; } - else if (sePos.contains(inputSectionPos)) + else if (DhSectionPos.contains(sePos, inputSectionPos)) { // TODO merge duplicate code if (replaceValue && this.seChild == null) @@ -293,7 +294,7 @@ public class QuadNode public Iterator> getLeafNodeIterator() { return new QuadTreeNodeIterator<>(this, true); } /** positions can point to null children */ - public Iterator getChildPosIterator() { return new QuadNodeDirectChildPosIterator<>(this); } + public LongIterator getChildPosIterator() { return new QuadNodeDirectChildPosIterator<>(this); } public Iterator> getChildNodeIterator() { return new QuadNodeDirectChildIterator<>(this); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java index 1679bea9e..8bd84e060 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java @@ -28,13 +28,13 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.core.util.gridList.MovableGridRingList; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; +import it.unimi.dsi.fastutil.longs.LongIterator; import org.apache.logging.log4j.Logger; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.NoSuchElementException; -import java.util.Queue; +import java.util.*; import java.util.function.Consumer; +import java.util.function.LongConsumer; /** * This class represents a quadTree of T type values. @@ -96,9 +96,9 @@ public class QuadTree //=====================// /** @return the node at the given section position */ - public final QuadNode getNode(DhSectionPos pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); } + public final QuadNode getNode(long pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); } /** @return the value at the given section position */ - public final T getValue(DhSectionPos pos) throws IndexOutOfBoundsException + public final T getValue(long pos) throws IndexOutOfBoundsException { QuadNode node = this.getNode(pos); if (node != null) @@ -109,7 +109,7 @@ public class QuadTree } /** @return the value that was previously in the given position, null if nothing */ - public final T setValue(DhSectionPos pos, T value) throws IndexOutOfBoundsException + public final T setValue(long pos, T value) throws IndexOutOfBoundsException { T previousValue = this.getValue(pos); this.getOrSetNode(pos, true, value, true); @@ -117,21 +117,21 @@ public class QuadTree } /** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */ - protected final QuadNode getOrSetNode(DhSectionPos pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException + protected final QuadNode getOrSetNode(long pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException { 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.convertNewToDetailLevel(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: [" + DhSectionPos.toString(pos) + "] = block pos: " + DhSectionPos.convertToDetailLevel(pos, LodUtil.BLOCK_DETAIL_LEVEL)); } - DhSectionPos rootPos = pos.convertNewToDetailLevel(this.treeMinDetailLevel); - int ringListPosX = rootPos.getX(); - int ringListPosZ = rootPos.getZ(); + long rootPos = DhSectionPos.convertToDetailLevel(pos, this.treeMinDetailLevel); + int ringListPosX = DhSectionPos.getX(rootPos); + int ringListPosZ = DhSectionPos.getZ(rootPos); QuadNode topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ); if (topQuadNode == null) @@ -149,7 +149,7 @@ public class QuadTree } } - if (!topQuadNode.sectionPos.contains(pos)) + if (!DhSectionPos.contains(topQuadNode.sectionPos, pos)) { LodUtil.assertNotReach("failed to get a root node that contains the input position: " + pos + " root node pos: " + topQuadNode.sectionPos); } @@ -163,10 +163,10 @@ public class QuadTree return returnNode; } - public boolean isSectionPosInBounds(DhSectionPos testPos) + public boolean isSectionPosInBounds(long testPos) { // check if the testPos is within the detail level limits of the tree - boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= testPos.getDetailLevel() && testPos.getDetailLevel() <= this.treeMinDetailLevel; + boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= DhSectionPos.getDetailLevel(testPos) && DhSectionPos.getDetailLevel(testPos) <= this.treeMinDetailLevel; if (!detailLevelWithinBounds) { return false; @@ -177,9 +177,9 @@ public class QuadTree DhBlockPos2D treeBlockCorner = this.centerBlockPos.add(new DhBlockPos2D(-this.diameterInBlocks / 2, -this.diameterInBlocks / 2)); DhLodPos treeCornerPos = new DhLodPos((byte) 0, treeBlockCorner.x, treeBlockCorner.z); - DhSectionPos inputSectionCorner = testPos.convertNewToDetailLevel((byte) 0); - DhLodPos inputCornerPos = new DhLodPos((byte) 0, inputSectionCorner.getX(), inputSectionCorner.getZ()); - int inputBlockWidth = BitShiftUtil.powerOfTwo(testPos.getDetailLevel()); + long inputSectionCorner = DhSectionPos.convertToDetailLevel(testPos, (byte) 0); + DhLodPos inputCornerPos = new DhLodPos((byte) 0, DhSectionPos.getX(inputSectionCorner), DhSectionPos.getZ(inputSectionCorner)); + int inputBlockWidth = BitShiftUtil.powerOfTwo(DhSectionPos.getDetailLevel(testPos)); return DoSquaresOverlap(treeCornerPos, this.diameterInBlocks, inputCornerPos, inputBlockWidth); } @@ -205,13 +205,13 @@ public class QuadTree } - public int getNonNullChildCountAtPos(DhSectionPos pos) { return this.getChildCountAtPos(pos, false); } - public int getChildCountAtPos(DhSectionPos pos, boolean includeNullValues) + public int getNonNullChildCountAtPos(long pos) { return this.getChildCountAtPos(pos, false); } + public int getChildCountAtPos(long pos, boolean includeNullValues) { int childCount = 0; for (int i = 0; i < 4; i++) { - DhSectionPos childPos = pos.getChildByIndex(i); + long childPos = DhSectionPos.getChildByIndex(pos, i); if (this.isSectionPosInBounds(childPos)) { T value = this.getValue(childPos); @@ -232,7 +232,7 @@ public class QuadTree //===========// /** can include null nodes */ - public Iterator rootNodePosIterator() { return new QuadTreeRootPosIterator(true); } + public LongIterator rootNodePosIterator() { return new QuadTreeRootPosIterator(true); } public Iterator> nodeIterator() { return new QuadTreeNodeIterator(false); } public Iterator> leafNodeIterator() { return new QuadTreeNodeIterator(true); } @@ -397,9 +397,9 @@ public class QuadTree // iterator classes // //==================// - private class QuadTreeRootPosIterator implements Iterator + private class QuadTreeRootPosIterator implements LongIterator { - private final Queue iteratorPosQueue = new LinkedList<>(); + private final LongArrayFIFOQueue iteratorPosQueue = new LongArrayFIFOQueue(); @@ -409,11 +409,10 @@ public class QuadTree { if (node != null || includeNullNodes) { - // TODO can these DhSectionPos be pooled? - DhSectionPos rootPos = new DhSectionPos(QuadTree.this.treeMinDetailLevel, pos2D.x, pos2D.y); + long rootPos = DhSectionPos.encode(QuadTree.this.treeMinDetailLevel, pos2D.x, pos2D.y); if (QuadTree.this.isSectionPosInBounds(rootPos)) { - this.iteratorPosQueue.add(rootPos); + this.iteratorPosQueue.enqueue(rootPos); } } }); @@ -425,7 +424,7 @@ public class QuadTree public boolean hasNext() { return this.iteratorPosQueue.size() != 0; } @Override - public DhSectionPos next() + public long nextLong() { if (this.iteratorPosQueue.size() == 0) { @@ -433,7 +432,7 @@ public class QuadTree } - DhSectionPos sectionPos = this.iteratorPosQueue.poll(); + long sectionPos = this.iteratorPosQueue.dequeueLong(); return sectionPos; } @@ -443,7 +442,7 @@ public class QuadTree public void remove() { throw new UnsupportedOperationException("remove"); } @Override - public void forEachRemaining(Consumer action) { Iterator.super.forEachRemaining(action); } + public void forEachRemaining(LongConsumer action) { LongIterator.super.forEachRemaining(action); } } @@ -501,7 +500,7 @@ public class QuadTree Iterator> nodeIterator = null; while ((nodeIterator == null || !nodeIterator.hasNext()) && this.rootNodeIterator.hasNext()) { - DhSectionPos sectionPos = this.rootNodeIterator.next(); + long sectionPos = this.rootNodeIterator.nextLong(); QuadNode rootNode = QuadTree.this.getNode(sectionPos); if (rootNode != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java index 639f8e40a..bc014863b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeChildIndexIterator.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.util.objects.quadTree.iterators; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; import java.util.Iterator; @@ -36,7 +37,7 @@ public class QuadNodeChildIndexIterator implements Iterator public QuadNodeChildIndexIterator(QuadNode parentNode, boolean returnNullChildPos) { // only get the children if this section isn't at the bottom of the tree - if (parentNode.sectionPos.getDetailLevel() > parentNode.minimumDetailLevel) + if (DhSectionPos.getDetailLevel(parentNode.sectionPos) > parentNode.minimumDetailLevel) { // go over each child pos for (int i = 0; i < 4; i++) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeDirectChildPosIterator.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeDirectChildPosIterator.java index 87dba9afe..f304c0937 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeDirectChildPosIterator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadNodeDirectChildPosIterator.java @@ -21,12 +21,12 @@ package com.seibel.distanthorizons.core.util.objects.quadTree.iterators; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; +import it.unimi.dsi.fastutil.longs.LongIterator; -import java.util.Iterator; import java.util.NoSuchElementException; -import java.util.function.Consumer; +import java.util.function.LongConsumer; -public class QuadNodeDirectChildPosIterator implements Iterator +public class QuadNodeDirectChildPosIterator implements LongIterator { private final QuadNodeChildIndexIterator childIndexIterator; private final QuadNode parentNode; @@ -44,7 +44,7 @@ public class QuadNodeDirectChildPosIterator implements Iterator public boolean hasNext() { return this.childIndexIterator.hasNext(); } @Override - public DhSectionPos next() + public long nextLong() { if (!this.hasNext()) { @@ -53,7 +53,7 @@ public class QuadNodeDirectChildPosIterator implements Iterator int childIndex = this.childIndexIterator.next(); - DhSectionPos sectionPos = this.parentNode.sectionPos.getChildByIndex(childIndex); + long sectionPos = DhSectionPos.getChildByIndex(this.parentNode.sectionPos, childIndex); return sectionPos; } @@ -63,6 +63,6 @@ public class QuadNodeDirectChildPosIterator implements Iterator public void remove() { throw new UnsupportedOperationException("remove"); } @Override - public void forEachRemaining(Consumer action) { Iterator.super.forEachRemaining(action); } + public void forEachRemaining(LongConsumer action) { LongIterator.super.forEachRemaining(action); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java index 13745870e..54fb3ae01 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/iterators/QuadTreeNodeIterator.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.util.objects.quadTree.iterators; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; import java.util.Iterator; @@ -46,7 +47,7 @@ public class QuadTreeNodeIterator implements Iterator> this.onlyReturnLeafValues = onlyReturnLeafValues; // TODO the naming conversion for these are flipped in a lot of places this.highestDetailLevel = rootNode.minimumDetailLevel; - this.iteratorDetailLevel = rootNode.sectionPos.getDetailLevel(); + this.iteratorDetailLevel = DhSectionPos.getDetailLevel(rootNode.sectionPos); if (!this.onlyReturnLeafValues) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/PositionalLockProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/PositionalLockProvider.java index 515c8c04a..c169a9474 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/PositionalLockProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/PositionalLockProvider.java @@ -32,7 +32,7 @@ public class PositionalLockProvider private static final int MAX_NUMBER_OF_LOCKS = 100; - private final ConcurrentHashMap lockByPos = new ConcurrentHashMap<>(); + private final ConcurrentHashMap lockByPos = new ConcurrentHashMap<>(); private final AtomicBoolean lockRemovalThreadRunning = new AtomicBoolean(false); @@ -50,7 +50,7 @@ public class PositionalLockProvider // getter // //========// - public ReentrantLock getLock(DhSectionPos pos) + public ReentrantLock getLock(long pos) { return this.lockByPos.compute(pos, (ignorePos, lock) -> { @@ -76,14 +76,14 @@ public class PositionalLockProvider Thread.sleep(CLEANUP_THREAD_MAX_FREQUENCY_IN_MS); // walk over every lock and check which ones need to be removed - Iterator keySet = this.lockByPos.keySet().iterator(); + Iterator keySet = this.lockByPos.keySet().iterator(); while (keySet.hasNext()) { try { long currentTime = System.currentTimeMillis(); - DhSectionPos pos = keySet.next(); + long pos = keySet.next(); ExpiringLock lock = this.lockByPos.get(pos); // don't try removing a lock that's currently in use @@ -92,7 +92,7 @@ public class PositionalLockProvider if (currentTime > lock.expirationTimeInMs) { this.lockByPos.remove(pos); - //LOGGER.info("removed lock: "+pos); + //LOGGER.info("removed lock: "+DhSectionPos.toString(pos)); } lock.unlock(); } diff --git a/core/src/test/java/tests/CompressionTest.java b/core/src/test/java/tests/CompressionTest.java index b3d3de3a8..d871b58ac 100644 --- a/core/src/test/java/tests/CompressionTest.java +++ b/core/src/test/java/tests/CompressionTest.java @@ -21,15 +21,14 @@ package tests; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo; +import it.unimi.dsi.fastutil.longs.LongArrayList; import org.junit.Assert; import java.io.*; import java.text.CharacterIterator; import java.text.StringCharacterIterator; -import java.util.ArrayList; /** * Note: @@ -272,7 +271,7 @@ public class CompressionTest - ArrayList positionList = uncompressedRepo.getAllPositions(); + LongArrayList positionList = uncompressedRepo.getAllPositions(); totalUncompressedFileSizeInBytes = uncompressedRepo.getTotalDataSizeInBytes(); System.out.println("Found [" + positionList.size() + "] DTOs."); @@ -282,7 +281,7 @@ public class CompressionTest { try { - DhSectionPos pos = positionList.get(i); + long pos = positionList.getLong(i); if (i % 20 == 0) { System.out.println(i + "/" + maxTestPosition); diff --git a/core/src/test/java/tests/DhSectionPosTest.java b/core/src/test/java/tests/DhSectionPosTest.java index ff2419d1b..c0585a60e 100644 --- a/core/src/test/java/tests/DhSectionPosTest.java +++ b/core/src/test/java/tests/DhSectionPosTest.java @@ -19,99 +19,143 @@ package tests; -import com.seibel.distanthorizons.core.pos.*; +import com.seibel.distanthorizons.core.enums.EDhDirection; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import org.junit.Assert; import org.junit.Test; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; + public class DhSectionPosTest { + @Test - public void ContainsPosTest() + public void basicEncodeDecodeTest() { - DhSectionPos root = new DhSectionPos((byte) 10, 0, 0); - DhSectionPos child = new DhSectionPos((byte) 9, 1, 1); + long pos; - Assert.assertTrue("section pos contains fail", root.contains(child)); - Assert.assertFalse("section pos contains fail", child.contains(root)); + // zero pos + pos = DhSectionPos.encode((byte) 0, 0, 0); + assertSectionPosEqual(0, DhSectionPos.getDetailLevel(pos)); + assertSectionPosEqual(0, DhSectionPos.getX(pos)); + assertSectionPosEqual(0, DhSectionPos.getZ(pos)); + + // positive values + pos = DhSectionPos.encode((byte) 10, 4, 1); + assertSectionPosEqual(10, DhSectionPos.getDetailLevel(pos)); + assertSectionPosEqual(4, DhSectionPos.getX(pos)); + assertSectionPosEqual(1, DhSectionPos.getZ(pos)); + + // negative position, positive detail level + pos = DhSectionPos.encode((byte) 2, -1, -4); + assertSectionPosEqual(2, DhSectionPos.getDetailLevel(pos)); + assertSectionPosEqual(-1, DhSectionPos.getX(pos)); + assertSectionPosEqual(-4, DhSectionPos.getZ(pos)); + + } + + + + @Test + public void containsPosTest() + { + long root = DhSectionPos.encode((byte) 10, 0, 0); + long child = DhSectionPos.encode((byte) 9, 1, 1); + + Assert.assertTrue("section pos contains fail", DhSectionPos.contains(root, child)); + Assert.assertFalse("section pos contains fail", DhSectionPos.contains(child, root)); - root = new DhSectionPos((byte) 10, 1, 0); + root = DhSectionPos.encode((byte) 10, 1, 0); // out of bounds - child = new DhSectionPos((byte) 9, 0, 0); - Assert.assertFalse("position should be out of bounds", root.contains(child)); - child = new DhSectionPos((byte) 9, 1, 1); - Assert.assertFalse("position should be out of bounds", root.contains(child)); + child = DhSectionPos.encode((byte) 9, 0, 0); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); + child = DhSectionPos.encode((byte) 9, 1, 1); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); // in bounds - child = new DhSectionPos((byte) 9, 2, 0); - Assert.assertTrue("position should be in bounds", root.contains(child)); - child = new DhSectionPos((byte) 9, 3, 1); - Assert.assertTrue("position should be in bounds", root.contains(child)); + child = DhSectionPos.encode((byte) 9, 2, 0); + Assert.assertTrue("position should be in bounds", DhSectionPos.contains(root, child)); + child = DhSectionPos.encode((byte) 9, 3, 1); + Assert.assertTrue("position should be in bounds", DhSectionPos.contains(root, child)); // out of bounds - child = new DhSectionPos((byte) 9, 2, 2); - Assert.assertFalse("position should be out of bounds", root.contains(child)); - child = new DhSectionPos((byte) 9, 3, 3); - Assert.assertFalse("position should be out of bounds", root.contains(child)); + child = DhSectionPos.encode((byte) 9, 2, 2); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); + child = DhSectionPos.encode((byte) 9, 3, 3); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); - child = new DhSectionPos((byte) 9, 4, 4); - Assert.assertFalse("position should be out of bounds", root.contains(child)); - child = new DhSectionPos((byte) 9, 5, 5); - Assert.assertFalse("position should be out of bounds", root.contains(child)); + child = DhSectionPos.encode((byte) 9, 4, 4); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); + child = DhSectionPos.encode((byte) 9, 5, 5); + Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child)); + + + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 0, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 1, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 2, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 3, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 4, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 5, 0, 0))); + Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 6, 0, 0))); } @Test - public void ContainsAdjacentPosTest() + public void containsAdjacentPosTest() { // neither should contain the other, they are single blocks that are next to each other - DhSectionPos left = new DhSectionPos((byte) 0, 4606, 0); - DhSectionPos right = new DhSectionPos((byte) 0, 4607, 0); - Assert.assertFalse(left.contains(right)); - Assert.assertFalse(right.contains(left)); - - + long left = DhSectionPos.encode((byte) 0, 4606, 0); + long right = DhSectionPos.encode((byte) 0, 4607, 0); + Assert.assertFalse(DhSectionPos.contains(left, right)); + Assert.assertFalse(DhSectionPos.contains(right, left)); + + // 512 block wide sections that are adjacent, but not overlapping - left = new DhSectionPos((byte) 9, 0, 0); - right = new DhSectionPos((byte) 9, 1, 0); - Assert.assertFalse(left.contains(right)); - Assert.assertFalse(right.contains(left)); - + left = DhSectionPos.encode((byte) 9, 0, 0); + right = DhSectionPos.encode((byte) 9, 1, 0); + Assert.assertFalse(DhSectionPos.contains(left, right)); + Assert.assertFalse(DhSectionPos.contains(right, left)); + } @Test - public void ParentPosTest() + public void parentPosTest() { - DhSectionPos leaf = new DhSectionPos((byte) 0, 0, 0); - 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.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.convertNewToDetailLevel((byte) 2); - parent = leaf.getParentPos(); - Assert.assertEquals("parent upscale fail", convert, parent); - convert = leaf.convertNewToDetailLevel((byte) 0); - DhSectionPos childIndex = leaf.getChildByIndex(0); - Assert.assertEquals("child detail fail", convert, childIndex); - + long leaf = DhSectionPos.encode((byte) 0, 0, 0); + long convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 1); + long parent = DhSectionPos.getParentPos(leaf); + assertSectionPosEqual("get parent at 0,0 fail", convert, parent); + + + leaf = DhSectionPos.encode((byte) 0, 1, 1); + convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 1); + parent = DhSectionPos.getParentPos(leaf); + assertSectionPosEqual("get parent at 1,1 fail", convert, parent); + + + leaf = DhSectionPos.encode((byte) 1, 2, 2); + convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 2); + parent = DhSectionPos.getParentPos(leaf); + assertSectionPosEqual("parent upscale fail", convert, parent); + convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 0); + long childIndex = DhSectionPos.getChildByIndex(leaf, 0); + assertSectionPosEqual("child detail fail", convert, childIndex); + } @Test - public void ChildPosTest() + public void childPosTest() { - DhSectionPos node = new DhSectionPos((byte) 1, 2302, 0); - DhSectionPos nw = node.getChildByIndex(0); - DhSectionPos sw = node.getChildByIndex(1); - DhSectionPos ne = node.getChildByIndex(2); - DhSectionPos se = node.getChildByIndex(3); + long node = DhSectionPos.encode((byte) 1, 2302, 0); + long nw = DhSectionPos.getChildByIndex(node, 0); + long sw = DhSectionPos.getChildByIndex(node, 1); + long ne = DhSectionPos.getChildByIndex(node, 2); + long se = DhSectionPos.getChildByIndex(node, 3); // confirm no children have the same values Assert.assertNotEquals(nw, sw); @@ -119,244 +163,337 @@ public class DhSectionPosTest Assert.assertNotEquals(ne, se); // confirm each child has the correct value - Assert.assertEquals(nw, new DhSectionPos((byte) 0, 4604, 0)); - Assert.assertEquals(sw, new DhSectionPos((byte) 0, 4605, 0)); - Assert.assertEquals(ne, new DhSectionPos((byte) 0, 4604, 1)); - Assert.assertEquals(se, new DhSectionPos((byte) 0, 4605, 1)); + assertSectionPosEqual(nw, DhSectionPos.encode((byte) 0, 4604, 0)); + assertSectionPosEqual(sw, DhSectionPos.encode((byte) 0, 4605, 0)); + assertSectionPosEqual(ne, DhSectionPos.encode((byte) 0, 4604, 1)); + assertSectionPosEqual(se, DhSectionPos.encode((byte) 0, 4605, 1)); } @Test - public void GetCenterTest() + public void getCenterBlock2DTest() { - DhSectionPos node = new DhSectionPos((byte) 1, 2303, 0); - DhBlockPos2D centerBlockPos = node.getCenterBlockPos(); - DhBlockPos2D expectedCenterNode = new DhBlockPos2D(4606, 0); - Assert.assertEquals("", expectedCenterNode, centerBlockPos); + long parentNode = DhSectionPos.encode((byte) 2, 1151, 0); // width 4 blocks + long inputPos = DhSectionPos.encode((byte) 0, 4606, 0); // width 1 block + Assert.assertTrue(DhSectionPos.contains(parentNode, inputPos)); + + DhBlockPos2D parentCenter = DhSectionPos.getCenterBlockPos(parentNode); + DhBlockPos2D inputCenter = DhSectionPos.getCenterBlockPos(inputPos); - - - node = new DhSectionPos((byte) 10, 0, 0); // 1024 blocks wide - centerBlockPos = node.getCenterBlockPos(); - expectedCenterNode = new DhBlockPos2D(1024 / 2, 1024 / 2); - Assert.assertEquals("", expectedCenterNode, centerBlockPos); + Assert.assertEquals(new DhBlockPos2D(4606, 2), parentCenter); + Assert.assertEquals(new DhBlockPos2D(4606, 0), inputCenter); } @Test - public void GetCenter2Test() - { - DhSectionPos parentNode = new DhSectionPos((byte) 2, 1151, 0); // width 4 blocks - DhSectionPos inputPos = new DhSectionPos((byte) 0, 4606, 0); // width 1 block - Assert.assertTrue(parentNode.contains(inputPos)); - - DhBlockPos2D parentCenter = parentNode.getCenterBlockPos(); - DhBlockPos2D inputCenter = inputPos.getCenterBlockPos(); - - Assert.assertEquals(new DhBlockPos2D(4606, 2), parentCenter); - Assert.assertEquals(new DhBlockPos2D(4606, 0), inputCenter); - - } - - @Test - public void CreateFromBlockPos() + 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); + long originsectionPos = DhSectionPos.encode(originBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originsectionPos); // offset pos // + long offsetSectionPos; DhBlockPos offsetBlockPos = new DhBlockPos(1000, 0, 42000); - DhSectionPos offsetSectionPos = new DhSectionPos(offsetBlockPos); - Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(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); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } @Test - public void CreateFromBlockPos2D() + 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); + long originSectionPos = DhSectionPos.encode(originBlockPos); + assertSectionPosEqual(DhSectionPos.encode(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); + long offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(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); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } @Test - public void CreateFromChunkPos() + 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); + long originSectionPos = DhSectionPos.encode(originChunkPos); + assertSectionPosEqual(DhSectionPos.encode(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); + long offsetSectionPos = DhSectionPos.encode(offsetChunkPos); + assertSectionPosEqual(DhSectionPos.encode(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); + offsetSectionPos = DhSectionPos.encode(offsetChunkPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } @Test - public void ConvertToDetailLevel() + public void convertToDetailLevel() { // origin pos // - DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); + long originSectionPos = DhSectionPos.encode((byte) 0,0,0); - originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(new DhSectionPos((byte) 1, 0, 0), originSectionPos); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1); + assertSectionPosEqual(DhSectionPos.encode((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 = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(DhSectionPos.encode(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); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, 0, 0), originSectionPos); // offset pos // - DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + long offsetSectionPos = DhSectionPos.encode((byte) 0,-10000,5000); - sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(new DhSectionPos((byte) 1, -5000, 2500), sectionPos); + offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, (byte) 1); + assertSectionPosEqual(DhSectionPos.encode((byte) 1, -5000, 2500), offsetSectionPos); - sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -157, 78), sectionPos); + offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -157, 78), offsetSectionPos); - sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); - Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, -1, 0), sectionPos); + offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, -1, 0), offsetSectionPos); } @Test - public void GetOffsetWidth() + public void getOffsetWidth() { - DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); - DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + long originSectionPos = DhSectionPos.encode((byte) 0,0,0); + long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000); // 1 -> 0 byte returnDetailLevel = 0; - originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1); + assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel)); - sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1); + assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel)); // 2 -> 1 returnDetailLevel = 1; - originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 2); - Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 2); + assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel)); - sectionPos = sectionPos.convertNewToDetailLevel((byte) 2); - Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 2); + assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel)); // Block -> 0 returnDetailLevel = 0; - originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - Assert.assertEquals(64, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(64, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel)); - sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - Assert.assertEquals(64, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(64, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel)); // Region -> 3 returnDetailLevel = 3; - originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); - Assert.assertEquals(4096, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(4096, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel)); - sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL); - Assert.assertEquals(4096, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(4096, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel)); } @Test - public void GetBlockWidth() + public void getBlockWidth() { - DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); - DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + long originSectionPos = DhSectionPos.encode((byte) 0,0,0); + long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000); - Assert.assertEquals(1, originSectionPos.getBlockWidth()); - Assert.assertEquals(1, sectionPos.getBlockWidth()); + assertSectionPosEqual(1, DhSectionPos.getBlockWidth(originSectionPos)); + assertSectionPosEqual(1, DhSectionPos.getBlockWidth(sectionPos)); - originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(2, originSectionPos.getBlockWidth()); - sectionPos = sectionPos.convertNewToDetailLevel((byte) 1); - Assert.assertEquals(2, sectionPos.getBlockWidth()); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1); + assertSectionPosEqual(2, DhSectionPos.getBlockWidth(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1); + assertSectionPosEqual(2, DhSectionPos.getBlockWidth(sectionPos)); - 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 = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(64, DhSectionPos.getBlockWidth(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + assertSectionPosEqual(64, DhSectionPos.getBlockWidth(sectionPos)); - 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()); + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(32768, DhSectionPos.getBlockWidth(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + assertSectionPosEqual(32768, DhSectionPos.getBlockWidth(sectionPos)); } @Test - public void GetCenterBlockPos() + public void getCenterBlockPosOrigin() { - DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0); - DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000); + long originSectionPos = DhSectionPos.encode((byte) 0,0,0); + long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000); - Assert.assertEquals(new DhBlockPos2D(0, 0), originSectionPos.getCenterBlockPos()); - Assert.assertEquals(new DhBlockPos2D(-10000, 5000), sectionPos.getCenterBlockPos()); + + // 1x1 blocks + Assert.assertEquals(new DhBlockPos2D(0, 0), DhSectionPos.getCenterBlockPos(originSectionPos)); + Assert.assertEquals(new DhBlockPos2D(-10000, 5000), DhSectionPos.getCenterBlockPos(sectionPos)); - 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()); + // 2x2 blocks + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1); + Assert.assertEquals(new DhBlockPos2D(0, 0), DhSectionPos.getCenterBlockPos(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1); + Assert.assertEquals(new DhBlockPos2D(-10000, 5000), DhSectionPos.getCenterBlockPos(sectionPos)); + //sectionPos = DhSectionPos.encode((byte) 1, 2303, 0); + //Assert.assertEquals(new DhBlockPos2D(4606, 0), DhSectionPos.getCenterBlockPos(sectionPos)); - 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()); + // 4x4 blocks + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 2); + Assert.assertEquals(new DhBlockPos2D(2, 2), DhSectionPos.getCenterBlockPos(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 2); + Assert.assertEquals(new DhBlockPos2D(-9998, 5002), DhSectionPos.getCenterBlockPos(sectionPos)); - 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()); + // 64x64 blocks + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(32, 32), DhSectionPos.getCenterBlockPos(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(-10016, 5024), DhSectionPos.getCenterBlockPos(sectionPos)); + + + // 32,768 x 32,768 blocks + originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(16384, 16384), DhSectionPos.getCenterBlockPos(originSectionPos)); + sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL); + Assert.assertEquals(new DhBlockPos2D(-16384, 16384), DhSectionPos.getCenterBlockPos(sectionPos)); } + @Test + public void getMinCornerBlockPos() + { + long pos; + + // origin block detail + pos = DhSectionPos.encode((byte) 0,0,0); + Assert.assertEquals(0, DhSectionPos.getMinCornerBlockX(pos)); + Assert.assertEquals(0, DhSectionPos.getMinCornerBlockZ(pos)); + + // offset block detail + pos = DhSectionPos.encode((byte) 0,2,3); + Assert.assertEquals(2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos)); + Assert.assertEquals(3 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos)); + + // negative offset block detail + pos = DhSectionPos.encode((byte) 0,-1,-2); + Assert.assertEquals(-1 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos)); + Assert.assertEquals(-2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos)); + + + // origin chunk detail + pos = DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL,0,0); + Assert.assertEquals(0, DhSectionPos.getMinCornerBlockX(pos)); + Assert.assertEquals(0, DhSectionPos.getMinCornerBlockZ(pos)); + + // offset chunk detail + pos = DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL,2,3); + Assert.assertEquals(2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos)); + Assert.assertEquals(3 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos)); + + + } + + @Test + public void getAdjacentPos() + { + long pos = DhSectionPos.encode((byte) 0, 0, 0); + + assertSectionPosEqual(DhSectionPos.encode((byte) 0, 0, -1), DhSectionPos.getAdjacentPos(pos, EDhDirection.NORTH)); + assertSectionPosEqual(DhSectionPos.encode((byte) 0, 0, 1), DhSectionPos.getAdjacentPos(pos, EDhDirection.SOUTH)); + + assertSectionPosEqual(DhSectionPos.encode((byte) 0, 1, 0), DhSectionPos.getAdjacentPos(pos, EDhDirection.EAST)); + assertSectionPosEqual(DhSectionPos.encode((byte) 0, -1, 0), DhSectionPos.getAdjacentPos(pos, EDhDirection.WEST)); + + // getting the adjacent position in the up and down position don't make sense + Assert.assertThrows(IllegalArgumentException.class, () -> { DhSectionPos.getAdjacentPos(pos, EDhDirection.UP); }); + Assert.assertThrows(IllegalArgumentException.class, () -> { DhSectionPos.getAdjacentPos(pos, EDhDirection.DOWN); }); + } + + @Test + public void forEachChildIterator() + { + long pos = DhSectionPos.encode((byte) 1, 0, 0); + + ArrayList childPosList = new ArrayList<>(); + AtomicInteger childCount = new AtomicInteger(0); + DhSectionPos.forEachChild(pos, (childPos) -> + { + childCount.incrementAndGet(); + childPosList.add(childPos); + }); + + Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 0, 0))); + Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 1, 0))); + Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 0, 1))); + Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 1, 1))); + + } + + + + + //================// + // helper methods // + //================// + + public static void assertSectionPosEqual(long expected, long actual) { assertSectionPosEqual("", expected, actual); } + public static void assertSectionPosEqual(String messagePrefix, long expected, long actual) + { + if (!messagePrefix.endsWith(" ")) + { + messagePrefix += " "; + } + + String expectedString = DhSectionPos.toString(expected); + String actualString = DhSectionPos.toString(actual); + String mismatchSuffix = "expected: ["+expectedString+"] actual: ["+actualString+"]."; + + Assert.assertEquals(messagePrefix+"Detail level mismatch, "+mismatchSuffix, DhSectionPos.getDetailLevel(expected), DhSectionPos.getDetailLevel(actual)); + Assert.assertEquals(messagePrefix+"X Pos mismatch, "+mismatchSuffix, DhSectionPos.getX(expected), DhSectionPos.getX(actual)); + Assert.assertEquals(messagePrefix+"Z Pos mismatch, "+mismatchSuffix, DhSectionPos.getZ(expected), DhSectionPos.getZ(actual)); + } + + } diff --git a/core/src/test/java/tests/QuadTreeTest.java b/core/src/test/java/tests/QuadTreeTest.java index 722dfbaec..28cbfabb0 100644 --- a/core/src/test/java/tests/QuadTreeTest.java +++ b/core/src/test/java/tests/QuadTreeTest.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree; +import it.unimi.dsi.fastutil.longs.LongIterator; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; @@ -59,30 +60,30 @@ public class QuadTreeTest // (pseudo) root node // - testSet(tree, new DhSectionPos((byte) 10, 0, 0), 0); + testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 0); // first child (0,0) // - testSet(tree, new DhSectionPos((byte) 9, 0, 0), 1); - testSet(tree, new DhSectionPos((byte) 9, 1, 0), 2); - testSet(tree, new DhSectionPos((byte) 9, 0, 1), 3); - testSet(tree, new DhSectionPos((byte) 9, 1, 1), 4); + testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 9, 1, 0), 2); + testSet(tree, DhSectionPos.encode((byte) 9, 0, 1), 3); + testSet(tree, DhSectionPos.encode((byte) 9, 1, 1), 4); // second child (0,0) (0,0) // - testSet(tree, new DhSectionPos((byte) 8, 0, 0), 5); - testSet(tree, new DhSectionPos((byte) 8, 1, 0), 6); - testSet(tree, new DhSectionPos((byte) 8, 0, 1), 7); - testSet(tree, new DhSectionPos((byte) 8, 1, 1), 8); + testSet(tree, DhSectionPos.encode((byte) 8, 0, 0), 5); + testSet(tree, DhSectionPos.encode((byte) 8, 1, 0), 6); + testSet(tree, DhSectionPos.encode((byte) 8, 0, 1), 7); + testSet(tree, DhSectionPos.encode((byte) 8, 1, 1), 8); // second child (0,0) (1,1) // - testSet(tree, new DhSectionPos((byte) 8, 2, 2), 9); - testSet(tree, new DhSectionPos((byte) 8, 3, 2), 10); - testSet(tree, new DhSectionPos((byte) 8, 2, 3), 11); - testSet(tree, new DhSectionPos((byte) 8, 3, 3), 12); + testSet(tree, DhSectionPos.encode((byte) 8, 2, 2), 9); + testSet(tree, DhSectionPos.encode((byte) 8, 3, 2), 10); + testSet(tree, DhSectionPos.encode((byte) 8, 2, 3), 11); + testSet(tree, DhSectionPos.encode((byte) 8, 3, 3), 12); // third child (0,0) (1,0) (0,0) // - testSet(tree, new DhSectionPos((byte) 7, 5, 0), 9); - testSet(tree, new DhSectionPos((byte) 7, 6, 0), 10); - testSet(tree, new DhSectionPos((byte) 7, 5, 1), 11); - testSet(tree, new DhSectionPos((byte) 7, 6, 1), 12); + testSet(tree, DhSectionPos.encode((byte) 7, 5, 0), 9); + testSet(tree, DhSectionPos.encode((byte) 7, 6, 0), 10); + testSet(tree, DhSectionPos.encode((byte) 7, 5, 1), 11); + testSet(tree, DhSectionPos.encode((byte) 7, 6, 1), 12); } @@ -94,13 +95,13 @@ public class QuadTreeTest // root node // - testSet(tree, new DhSectionPos((byte) 10, -1, -1), 0); + testSet(tree, DhSectionPos.encode((byte) 10, -1, -1), 0); // first child (-1,-1) // - testSet(tree, new DhSectionPos((byte) 9, -2, -1), 1); - testSet(tree, new DhSectionPos((byte) 9, -1, -1), 2); - testSet(tree, new DhSectionPos((byte) 9, -2, -2), 3); - testSet(tree, new DhSectionPos((byte) 9, -1, -2), 4); + testSet(tree, DhSectionPos.encode((byte) 9, -2, -1), 1); + testSet(tree, DhSectionPos.encode((byte) 9, -1, -1), 2); + testSet(tree, DhSectionPos.encode((byte) 9, -2, -2), 3); + testSet(tree, DhSectionPos.encode((byte) 9, -1, -2), 4); // TODO // // second child (-1,-1) (0,0) // @@ -131,36 +132,36 @@ public class QuadTreeTest // wrong detail level on purpose, if the detail level was 0 (block) this should work - DhSectionPos outOfBoundsPos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0); + long outOfBoundsPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); // out of bounds // - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) + 1, 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) + 1, 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0); testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class); Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount()); // in bounds // - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 1, 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 1, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 3, 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 3, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount()); // TODO this position probably has trouble with getting the center. - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 2, 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 2, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 3, tree.leafNodeCount()); - outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 4, 0); + outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 4, 0); testSet(tree, outOfBoundsPos, 0); Assert.assertEquals("incorrect leaf node count", 4, tree.leafNodeCount()); @@ -173,15 +174,15 @@ public class QuadTreeTest Assert.assertEquals("Test may need to be re-calculated for different max detail level.", 9, tree.treeMinDetailLevel); - DhSectionPos rootPos = new DhSectionPos((byte) 9, 0, -1); + long rootPos = DhSectionPos.encode((byte) 9, 0, -1); testSet(tree, rootPos, 1); // pos is in tree, but out of range - DhSectionPos midPos = new DhSectionPos((byte) 8, 0, -1); + long midPos = DhSectionPos.encode((byte) 8, 0, -1); testSet(tree, midPos, 2, IndexOutOfBoundsException.class); // pos is in tree, but out of range - DhSectionPos leafPos = new DhSectionPos((byte) 7, 0, -2); + long leafPos = DhSectionPos.encode((byte) 7, 0, -2); testSet(tree, leafPos, 3, IndexOutOfBoundsException.class); } @@ -197,13 +198,13 @@ public class QuadTreeTest // (pseudo) root nodes // - testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1); // first child (0,0) // - DhSectionPos nw = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0); - DhSectionPos ne = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 0); - DhSectionPos sw = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 1); - DhSectionPos se = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 1); + long nw = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0); + long ne = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 0); + long sw = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 1); + long se = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 1); testSet(tree, nw, 2); testSet(tree, ne, 3); @@ -259,12 +260,12 @@ public class QuadTreeTest Assert.assertEquals("Tree center incorrect", DhBlockPos2D.ZERO, tree.getCenterBlockPos()); // on the negative X edge - DhSectionPos edgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, -treeParams.getWidthInBlocks() / 2, 0); + long edgePos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, -treeParams.getWidthInBlocks() / 2, 0); testSet(tree, edgePos, 1); Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount()); // +1 root node from the negative X edge - DhSectionPos adjacentEdgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (-treeParams.getWidthInBlocks() / 2) + pseudoRootNodeWidthInBlocks, 0); + long adjacentEdgePos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (-treeParams.getWidthInBlocks() / 2) + pseudoRootNodeWidthInBlocks, 0); testSet(tree, adjacentEdgePos, 2); Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount()); @@ -284,24 +285,24 @@ public class QuadTreeTest // (pseudo) root nodes // - testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1); - testSet(tree, new DhSectionPos((byte) 10, 1, 0), 2); + testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 10, 1, 0), 2); // first child (0,0) // - testSet(tree, new DhSectionPos((byte) 9, 0, 0), 3); - testSet(tree, new DhSectionPos((byte) 9, 1, 0), 4); - testSet(tree, new DhSectionPos((byte) 9, 0, 1), 5); - testSet(tree, new DhSectionPos((byte) 9, 1, 1), 6); + testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 3); + testSet(tree, DhSectionPos.encode((byte) 9, 1, 0), 4); + testSet(tree, DhSectionPos.encode((byte) 9, 0, 1), 5); + testSet(tree, DhSectionPos.encode((byte) 9, 1, 1), 6); // root nodes int rootNodeCount = 0; - Iterator rootNodePosIterator = tree.rootNodePosIterator(); + LongIterator rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - QuadNode rootNode = tree.getNode(rootNodePosIterator.next()); + QuadNode rootNode = tree.getNode(rootNodePosIterator.nextLong()); if (rootNode != null) { rootNodeCount++; @@ -331,20 +332,19 @@ public class QuadTreeTest @Test public void NewQuadTreeIterationTest() { - AbstractTestTreeParams treeParams = new LargeTestTree(); - QuadNode rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL); + QuadNode rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL); - rootNode.setValue(new DhSectionPos((byte) 10, 0, 0), 0); + rootNode.setValue(DhSectionPos.encode((byte) 10, 0, 0), 0); - rootNode.setValue(new DhSectionPos((byte) 9, 0, 0), 1); - rootNode.setValue(new DhSectionPos((byte) 9, 1, 0), 1); - rootNode.setValue(new DhSectionPos((byte) 9, 0, 1), 1); - rootNode.setValue(new DhSectionPos((byte) 9, 1, 1), null); + rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 0), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 0), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 1), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 1), null); - rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), null); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), null); @@ -411,14 +411,14 @@ public class QuadTreeTest { AbstractTestTreeParams treeParams = new TinyTestTree(); final QuadTree tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0); // confirm the root node were added int rootNodeCount = 0; - Iterator rootNodePosIterator = tree.rootNodePosIterator(); + LongIterator rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - QuadNode rootNode = tree.getNode(rootNodePosIterator.next()); + QuadNode rootNode = tree.getNode(rootNodePosIterator.nextLong()); if (rootNode != null) { rootNodeCount++; @@ -431,7 +431,7 @@ public class QuadTreeTest rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - DhSectionPos rootNodePos = rootNodePosIterator.next(); + long rootNodePos = rootNodePosIterator.nextLong(); QuadNode rootNode = tree.getNode(rootNodePos); if (rootNode != null) { @@ -468,10 +468,10 @@ public class QuadTreeTest tree.setCenterBlockPos(treeMovePos); Assert.assertEquals("tree move failed, incorrect position after move", treeMovePos, tree.getCenterBlockPos()); - Iterator rootNodePosIterator = tree.rootNodePosIterator(); + LongIterator rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - testSet(tree, rootNodePosIterator.next(), 0); + testSet(tree, rootNodePosIterator.nextLong(), 0); } @@ -480,7 +480,7 @@ public class QuadTreeTest rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - QuadNode rootNode = tree.getNode(rootNodePosIterator.next()); + QuadNode rootNode = tree.getNode(rootNodePosIterator.nextLong()); if (rootNode != null) { rootNodeCount++; @@ -499,16 +499,16 @@ public class QuadTreeTest Assert.assertEquals("incorrect tree width", treeParams.getWidthInBlocks(), tree.diameterInBlocks()); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, -1), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, -1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class); int rootNodeCount = 0; - Iterator rootNodeIterator = tree.rootNodePosIterator(); + LongIterator rootNodeIterator = tree.rootNodePosIterator(); while (rootNodeIterator.hasNext()) { - QuadNode rootNode = tree.getNode(rootNodeIterator.next()); + QuadNode rootNode = tree.getNode(rootNodeIterator.nextLong()); if (rootNode != null) { rootNodeCount++; @@ -529,25 +529,25 @@ public class QuadTreeTest // 2x2 valid positions (overlap the tree's width) - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, 0), 0); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, -1), 0); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, -1), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, 0), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, -1), 0); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, -1), 0); // invalid positions - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, 1), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, 1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 1), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 0), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, -1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 0), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, -1), -1, IndexOutOfBoundsException.class); int rootNodeCount = 0; - Iterator rootNodeIterator = tree.rootNodePosIterator(); + LongIterator rootNodeIterator = tree.rootNodePosIterator(); while (rootNodeIterator.hasNext()) { - QuadNode rootNode = tree.getNode(rootNodeIterator.next()); + QuadNode rootNode = tree.getNode(rootNodeIterator.nextLong()); if (rootNode != null) { rootNodeCount++; @@ -565,17 +565,17 @@ public class QuadTreeTest Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMinDetailLevel); // valid detail levels - testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1); - testSet(tree, new DhSectionPos((byte) 9, 0, 0), 2); - testSet(tree, new DhSectionPos((byte) 8, 0, 0), 3); + testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 2); + testSet(tree, DhSectionPos.encode((byte) 8, 0, 0), 3); // detail level too low - testSet(tree, new DhSectionPos((byte) 7, 0, 0), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos((byte) 6, 0, 0), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode((byte) 7, 0, 0), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode((byte) 6, 0, 0), -1, IndexOutOfBoundsException.class); // detail level too high - testSet(tree, new DhSectionPos((byte) 11, 0, 0), -1, IndexOutOfBoundsException.class); - testSet(tree, new DhSectionPos((byte) 12, 0, 0), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode((byte) 11, 0, 0), -1, IndexOutOfBoundsException.class); + testSet(tree, DhSectionPos.encode((byte) 12, 0, 0), -1, IndexOutOfBoundsException.class); } @@ -587,25 +587,25 @@ public class QuadTreeTest Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMinDetailLevel); // create the root node - testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1); AtomicInteger minimumDetailLevelReachedRef = new AtomicInteger(tree.treeMinDetailLevel); // recurse down the tree - Iterator rootNodePosIterator = tree.rootNodePosIterator(); + LongIterator rootNodePosIterator = tree.rootNodePosIterator(); while (rootNodePosIterator.hasNext()) { - DhSectionPos sectionPos = rootNodePosIterator.next(); + long sectionPos = rootNodePosIterator.nextLong(); QuadNode rootNode = tree.getNode(sectionPos); if (rootNode != null) { // fill in the root node's direct children - Iterator childPosIterator = rootNode.getChildPosIterator(); + LongIterator childPosIterator = rootNode.getChildPosIterator(); while (childPosIterator.hasNext()) { - DhSectionPos rootChildPos = childPosIterator.next(); + long rootChildPos = childPosIterator.nextLong(); rootNode.setValue(rootChildPos, 0); } @@ -618,7 +618,7 @@ public class QuadTreeTest QuadNode childNode = ChildIterator.next(); Assert.assertNotNull(childNode); // TODO is this correct? - recursivelyCreateNodeChildren(childNode, tree.treeMaxDetailLevel, minimumDetailLevelReachedRef); + this.recursivelyCreateNodeChildren(childNode, tree.treeMaxDetailLevel, minimumDetailLevelReachedRef); } } } @@ -634,10 +634,10 @@ public class QuadTreeTest // fill in the null children - Iterator directChildIterator = node.getChildPosIterator(); + LongIterator directChildIterator = node.getChildPosIterator(); while (directChildIterator.hasNext()) { - node.setValue(directChildIterator.next(), 0); + node.setValue(directChildIterator.nextLong(), 0); childNodesCreated = true; } @@ -646,10 +646,10 @@ public class QuadTreeTest directChildIterator = node.getChildPosIterator(); while (directChildIterator.hasNext()) { - DhSectionPos sectionPos = directChildIterator.next(); + long sectionPos = directChildIterator.nextLong(); QuadNode childNode = node.getNode(sectionPos); - Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + childNode.sectionPos.getDetailLevel(), childNode.sectionPos.getDetailLevel() >= minDetailLevel); + Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + DhSectionPos.getDetailLevel(childNode.sectionPos), DhSectionPos.getDetailLevel(childNode.sectionPos) >= 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.getDetailLevel() < minimumDetailLevelReachedRef.get()) + if ( DhSectionPos.getDetailLevel(node.sectionPos) < minimumDetailLevelReachedRef.get()) { - minimumDetailLevelReachedRef.set(node.sectionPos.getDetailLevel()); + minimumDetailLevelReachedRef.set( DhSectionPos.getDetailLevel(node.sectionPos)); } @@ -667,22 +667,22 @@ public class QuadTreeTest // assertions if (childNodesCreated) { - Assert.assertTrue("node children created below minimum detail level", node.sectionPos.getDetailLevel() >= minDetailLevel); + Assert.assertTrue("node children created below minimum detail level", DhSectionPos.getDetailLevel( node.sectionPos) >= minDetailLevel); } if (childNodesIterated) { - Assert.assertTrue("node children iterated below minimum detail level", node.sectionPos.getDetailLevel() - 1 >= minDetailLevel); + Assert.assertTrue("node children iterated below minimum detail level", DhSectionPos.getDetailLevel(node.sectionPos) - 1 >= minDetailLevel); } } @Test public void quadNodeChildPositionIndexTest() { - QuadNode rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), (byte) 0); - Iterator directChildPosIterator = rootNode.getChildPosIterator(); + QuadNode rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), (byte) 0); + LongIterator directChildPosIterator = rootNode.getChildPosIterator(); while (directChildPosIterator.hasNext()) { - DhSectionPos sectionPos = directChildPosIterator.next(); + long sectionPos = directChildPosIterator.nextLong(); Assert.assertNotEquals("Root node pos shouldn't be included in direct child pos iteration", sectionPos, rootNode.sectionPos); rootNode.setValue(sectionPos, 1); @@ -692,9 +692,9 @@ public class QuadTreeTest for (int i = 0; i < 4; i++) { - DhSectionPos childPos = rootNode.sectionPos.getChildByIndex(i); + long childPos = DhSectionPos.getChildByIndex(rootNode.sectionPos, i); QuadNode childNode = rootNode.getChildByIndex(i); - Assert.assertEquals("child position not the same as " + DhSectionPos.class.getSimpleName() + "'s getChildByIndex()", childPos, childNode.sectionPos); + Assert.assertEquals("child position not the same as " + long.class.getSimpleName() + "'s getChildByIndex()", childPos, childNode.sectionPos); } } @@ -708,7 +708,7 @@ public class QuadTreeTest // center root node - DhSectionPos centerNodePos = new DhSectionPos((byte) 1, 0, 0); + long centerNodePos = DhSectionPos.encode((byte) 1, 0, 0); // create node tree.setValue(centerNodePos, 0); @@ -716,10 +716,10 @@ public class QuadTreeTest Assert.assertNotNull(centerRootNode); // child pos in bounds of the tree - Iterator childPosIterator = centerRootNode.getChildPosIterator(); + LongIterator childPosIterator = centerRootNode.getChildPosIterator(); while (childPosIterator.hasNext()) { - DhSectionPos childPos = childPosIterator.next(); + long childPos = childPosIterator.nextLong(); centerRootNode.setValue(childPos, 1); } Assert.assertEquals("center node not filled", 4, centerRootNode.getNonNullChildCount()); @@ -727,7 +727,7 @@ public class QuadTreeTest // edge root node - DhSectionPos offsetNodePos = new DhSectionPos((byte) 1, -17, -16); + long offsetNodePos = DhSectionPos.encode((byte) 1, -17, -16); // create node tree.setValue(offsetNodePos, 0); @@ -738,7 +738,7 @@ public class QuadTreeTest childPosIterator = offsetRootNode.getChildPosIterator(); while (childPosIterator.hasNext()) { - DhSectionPos childPos = childPosIterator.next(); + long childPos = childPosIterator.nextLong(); offsetRootNode.setValue(childPos, 1); } // TODO James thought this shouldn't work for all 4 nodes, but he must've thought wrong. @@ -772,7 +772,7 @@ public class QuadTreeTest // - testSet(tree, new DhSectionPos((byte) 0, 0, 0), 1); + testSet(tree, DhSectionPos.encode((byte) 0, 0, 0), 1); Assert.assertEquals(1, tree.count()); tree.setCenterBlockPos(new DhBlockPos2D(treeWidth + (treeWidth / 2), 0)); Assert.assertEquals(0, tree.count()); @@ -783,21 +783,21 @@ public class QuadTreeTest //@Test public void autoDeleteNullQuadNodeChildTest() { - QuadNode rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL); + QuadNode rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL); - rootNode.setValue(new DhSectionPos((byte) 10, 0, 0), 0); + rootNode.setValue(DhSectionPos.encode((byte) 10, 0, 0), 0); - DhSectionPos midNodePos = new DhSectionPos((byte) 9, 0, 0); + long midNodePos = DhSectionPos.encode((byte) 9, 0, 0); //rootNode.setValue(midNodePos, null); // holds detail 8 - rootNode.setValue(new DhSectionPos((byte) 9, 1, 0), 1); - rootNode.setValue(new DhSectionPos((byte) 9, 0, 1), 1); - rootNode.setValue(new DhSectionPos((byte) 9, 1, 1), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 0), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 1), 1); + rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 1), 1); - rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), 2); - rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), 2); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), 2); @@ -810,12 +810,12 @@ public class QuadTreeTest // test removing nodes // // remove two leaf nodes from the root - DhSectionPos leafPos = new DhSectionPos((byte) 9, 1, 1); + long leafPos = DhSectionPos.encode((byte) 9, 1, 1); rootNode.setValue(leafPos, null); Assert.assertEquals(3, rootNode.getNonNullChildCount()); Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos)); - leafPos = new DhSectionPos((byte) 9, 0, 1); + leafPos = DhSectionPos.encode((byte) 9, 0, 1); rootNode.setValue(leafPos, null); Assert.assertEquals(2, rootNode.getNonNullChildCount()); Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos)); @@ -827,13 +827,13 @@ public class QuadTreeTest Assert.assertEquals(4, rootNode.getNode(midNodePos).getNonNullChildCount()); // remove all but one, mid-node should still be present - rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), null); - rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), null); - rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), null); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), null); + rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), null); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), null); Assert.assertEquals(1, rootNode.getNode(midNodePos).getNonNullChildCount()); // remove last mid-node child, mid-node should now be removed - rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), null); + rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), null); Assert.assertNull("Mid node not deleted.", rootNode.getNode(midNodePos)); Assert.assertEquals(3, rootNode.getNonNullChildCount()); @@ -847,8 +847,8 @@ public class QuadTreeTest // helper methods // //================// - private static void testSet(QuadTree tree, DhSectionPos pos, Integer setValue) { testSet(tree, pos, setValue, null); } - private static void testSet(QuadTree tree, DhSectionPos pos, Integer setValue, Class expectedExceptionClass) + private static void testSet(QuadTree tree, long pos, Integer setValue) { testSet(tree, pos, setValue, null); } + private static void testSet(QuadTree tree, long pos, Integer setValue, Class expectedExceptionClass) { // set try @@ -860,7 +860,7 @@ public class QuadTreeTest if (expectedExceptionClass == null || e.getClass() != expectedExceptionClass) { e.printStackTrace(); - Assert.fail("set failed " + pos + " with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage()); + Assert.fail("set failed [" + DhSectionPos.toString(pos) + "] with exception [" + e.getClass() + "], expected exception: [" + expectedExceptionClass + "]. error: " + e.getMessage()); } } @@ -869,20 +869,20 @@ public class QuadTreeTest testGet(tree, pos, setValue, expectedExceptionClass); } - private static void testGet(QuadTree tree, DhSectionPos pos, Integer getValue) { testSet(tree, pos, getValue, null); } - private static void testGet(QuadTree tree, DhSectionPos pos, Integer getValue, Class expectedExceptionClass) + private static void testGet(QuadTree tree, long pos, Integer getValue) { testSet(tree, pos, getValue, null); } + private static void testGet(QuadTree tree, long pos, Integer getValue, Class expectedExceptionClass) { try { Integer getResult = tree.getValue(pos); - Assert.assertEquals("get failed " + pos, getValue, getResult); + Assert.assertEquals("get failed [" + DhSectionPos.toString(pos) + "]", getValue, getResult); } catch (Exception e) { if (expectedExceptionClass == null || e.getClass() != expectedExceptionClass) { e.printStackTrace(); - Assert.fail("get failed " + pos + " with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage()); + Assert.fail("get failed [" + DhSectionPos.toString(pos) + "] with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage()); } } }