diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java index ebcb807e4..28e04c995 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java @@ -94,24 +94,31 @@ public interface IFullDataSource FullDataPointIdMap getMapping(); /** - * @param highestGeneratorDetailLevel the smallest numerical detail level that the un-generated positions should be split into + * @param generatorDetailLevel the detail level that the un-generated positions should be split into. + * @param onlyReturnPositionsTheGeneratorCanAccept + * If true this will only return positions with the same detail level as generatorDetailLevel.
+ * If false this will return the lowest detail level possible for totally empty sections.
+ * As of 2023-9-28 both have been tested and confirmed working with the Batch world generator, the only difference is that + * the task list will be deceptively small if this value is false. + * * @return the list of {@link DhSectionPos} that aren't generated in this data source. */ - default ArrayList getUngeneratedPosList(byte highestGeneratorDetailLevel, boolean onlyReturnPositionsTheGeneratorCanAccept) + default ArrayList getUngeneratedPosList(byte generatorDetailLevel, boolean onlyReturnPositionsTheGeneratorCanAccept) { - ArrayList posArray = this.getUngeneratedPosList(this.getSectionPos(), highestGeneratorDetailLevel); + ArrayList posArray = this.getUngeneratedPosListForQuadrant(this.getSectionPos(), generatorDetailLevel); if (onlyReturnPositionsTheGeneratorCanAccept) { LinkedList posList = new LinkedList<>(posArray); + // subdivide positions until they match the generatorDetailLevel ArrayList cleanedPosArray = new ArrayList<>(); while (posList.size() > 0) { DhSectionPos pos = posList.remove(); - if (pos.getDetailLevel() > highestGeneratorDetailLevel) + if (pos.getDetailLevel() > generatorDetailLevel) { - pos.forEachChild((childPos) -> { posList.push(childPos); }); + pos.forEachChild((childPos) -> posList.push(childPos)); } else { @@ -126,142 +133,131 @@ public interface IFullDataSource return posArray; } } - default ArrayList getUngeneratedPosList(DhSectionPos quadrantPos, byte highestGeneratorDetailLevel) + default ArrayList getUngeneratedPosListForQuadrant(DhSectionPos quadrantPos, byte generatorDetailLevel) { ArrayList ungeneratedPosList = new ArrayList<>(); - int sourceRelWidth = this.getWidthInDataPoints(); + int sourceRelWidthInDataPoints = this.getWidthInDataPoints(); - if (quadrantPos.getDetailLevel() < highestGeneratorDetailLevel) - { - throw new IllegalArgumentException("detail level lower than world generator can accept."); - } - else if (quadrantPos.getDetailLevel() == highestGeneratorDetailLevel) + if (quadrantPos.getDetailLevel() == generatorDetailLevel) { // we are at the highest detail level the world generator can accept, // we either need to generate this whole section, or not at all - // TODO combine duplicate code - - byte childDetailLevel = (byte) (quadrantPos.getDetailLevel()); - - int quadrantDetailLevelDiff = this.getSectionPos().getDetailLevel() - childDetailLevel; - int widthInSecPos = BitShiftUtil.powerOfTwo(quadrantDetailLevelDiff); - int relWidthForSecPos = sourceRelWidth / widthInSecPos; - - DhSectionPos minSecPos = this.getSectionPos().convertNewToDetailLevel(childDetailLevel); - DhSectionPos inputPos = quadrantPos; - - - - int minRelX = inputPos.getX() - minSecPos.getX(); - int minRelZ = inputPos.getZ() - minSecPos.getZ(); - int maxRelX = minRelX + 1; - int maxRelZ = minRelZ + 1; - - minRelX = minRelX * relWidthForSecPos; - minRelZ = minRelZ * relWidthForSecPos; - maxRelX = maxRelX * relWidthForSecPos; - maxRelZ = maxRelZ * relWidthForSecPos; - - - boolean quadrantFullyGenerated = true; - for (int relX = minRelX; relX < maxRelX; relX++) - { - for (int relZ = minRelZ; relZ < maxRelZ; relZ++) - { - SingleColumnFullDataAccessor column = this.tryGet(relX, relZ); - if (column == null || !column.doesColumnExist()) - { - // no data for this relative position - quadrantFullyGenerated = false; - break; - } - } - } - - if (!quadrantFullyGenerated) + ESectionPopulationState populationState = this.getPopulationStateForPos(quadrantPos, sourceRelWidthInDataPoints); + if (populationState != ESectionPopulationState.COMPLETE) { // at least 1 data point is missing, - // this whole section must be regenerated + // this whole section must be generated ungeneratedPosList.add(quadrantPos); } } - else + else if (quadrantPos.getDetailLevel() > generatorDetailLevel) { - // TODO comment - // TODO combine duplicate code - - byte childDetailLevel = (byte) (quadrantPos.getDetailLevel() - 1); - + // detail level too low for world generator, check child positions for (int i = 0; i < 4; i++) { - int quadrantDetailLevelDiff = this.getSectionPos().getDetailLevel() - childDetailLevel; - int widthInSecPos = BitShiftUtil.powerOfTwo(quadrantDetailLevelDiff); - int relWidthForSecPos = sourceRelWidth / widthInSecPos; - - DhSectionPos minSecPos = this.getSectionPos().convertNewToDetailLevel(childDetailLevel); DhSectionPos inputPos = quadrantPos.getChildByIndex(i); - - - int minRelX = inputPos.getX() - minSecPos.getX(); - int minRelZ = inputPos.getZ() - minSecPos.getZ(); - int maxRelX = minRelX + 1; - int maxRelZ = minRelZ + 1; - - minRelX = minRelX * relWidthForSecPos; - minRelZ = minRelZ * relWidthForSecPos; - maxRelX = maxRelX * relWidthForSecPos; - maxRelZ = maxRelZ * relWidthForSecPos; - - - - boolean quadrantFullyGenerated = true; - boolean quadrantEmpty = true; - for (int relX = minRelX; relX < maxRelX; relX++) - { - for (int relZ = minRelZ; relZ < maxRelZ; relZ++) - { - SingleColumnFullDataAccessor column = this.tryGet(relX, relZ); - if (column == null || !column.doesColumnExist()) - { - // no data for this relative position - quadrantFullyGenerated = false; - } - else - { - // data exists for this pos - quadrantEmpty = false; - } - } - } - - - if (quadrantFullyGenerated) + ESectionPopulationState populationState = this.getPopulationStateForPos(inputPos, sourceRelWidthInDataPoints); + if (populationState == ESectionPopulationState.COMPLETE) { // no generation necessary continue; } - else if (quadrantEmpty) + else if (populationState == ESectionPopulationState.EMPTY) { // nothing exists for this sub quadrant, add this sub-quadrant's position ungeneratedPosList.add(inputPos); } - else + else if (populationState == ESectionPopulationState.PARTIAL) { // some data exists in this quadrant, but not all that we need // recurse down to determine which sub-quadrant positions will need generation - - ungeneratedPosList.addAll(this.getUngeneratedPosList(inputPos, highestGeneratorDetailLevel)); + ungeneratedPosList.addAll(this.getUngeneratedPosListForQuadrant(inputPos, generatorDetailLevel)); } - } } + else + { + throw new IllegalArgumentException("detail level lower than world generator can accept."); + } return ungeneratedPosList; } + default ESectionPopulationState getPopulationStateForPos(DhSectionPos inputPos, int sourceRelWidthInDataPoints) + { + // TODO comment + + byte childDetailLevel = inputPos.getDetailLevel(); + + int quadrantDetailLevelDiff = this.getSectionPos().getDetailLevel() - childDetailLevel; + int widthInSecPos = BitShiftUtil.powerOfTwo(quadrantDetailLevelDiff); + int relWidthForSecPos = sourceRelWidthInDataPoints / widthInSecPos; + + DhSectionPos minSecPos = this.getSectionPos().convertNewToDetailLevel(childDetailLevel); + + + + int minRelX = inputPos.getX() - minSecPos.getX(); + int maxRelX = minRelX + 1; + + int minRelZ = inputPos.getZ() - minSecPos.getZ(); + int maxRelZ = minRelZ + 1; + + minRelX = minRelX * relWidthForSecPos; + maxRelX = maxRelX * relWidthForSecPos; + + minRelZ = minRelZ * relWidthForSecPos; + maxRelZ = maxRelZ * relWidthForSecPos; + + + + boolean quadrantFullyGenerated = true; + boolean quadrantEmpty = true; + for (int relX = minRelX; relX < maxRelX; relX++) + { + for (int relZ = minRelZ; relZ < maxRelZ; relZ++) + { + SingleColumnFullDataAccessor column = this.tryGet(relX, relZ); + if (column == null || !column.doesColumnExist()) + { + // no data for this relative position + quadrantFullyGenerated = false; + } + else + { + // data exists for this pos + quadrantEmpty = false; + } + } + } + + + if (quadrantFullyGenerated) + { + // no generation necessary + return ESectionPopulationState.COMPLETE; + } + else if (quadrantEmpty) + { + // nothing exists for this sub quadrant, add this sub-quadrant's position + return ESectionPopulationState.EMPTY; + } + else + { + // some data exists in this quadrant, but not all that we need + // recurse down to determine which sub-quadrant positions will need generation + return ESectionPopulationState.PARTIAL; + } + } + enum ESectionPopulationState + { + COMPLETE, + EMPTY, + PARTIAL + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index a0020773a..14ba81d37 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -67,7 +67,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I public boolean doesFileExist; - //TODO: Atm can't find a better way to store when genQueue is checked. + /** indicates if this file has been checked for missing sections to generate or not */ public boolean genQueueChecked = false; public AbstractFullDataSourceLoader fullDataSourceLoader; 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 92b8f1947..0b48e5ce1 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 @@ -57,7 +57,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender private final IDhApiWorldGenerator generator; /** contains the positions that need to be generated */ - //private final QuadTree waitingTaskQuadTree; private final ConcurrentHashMap waitingTasks = new ConcurrentHashMap<>(); private final ConcurrentHashMap inProgressGenTasksByLodPos = new ConcurrentHashMap<>(); @@ -115,11 +114,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender this.largestDataDetail = generator.getLargestDataDetailLevel(); this.smallestDataDetail = generator.getSmallestDataDetailLevel(); - //FIXME: Currently resizing view dist doesn't update this, causing some gen task to fail. - int treeWidth = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH * 2; // TODO the *2 is to allow for generation edge cases, and should probably be removed at some point - byte treeMinDetailLevel = LodUtil.CHUNK_DETAIL_LEVEL; // The min level should be at least fill in 1 ChunkSizedFullDataAccessor. - //this.waitingTaskQuadTree = new QuadTree<>(treeWidth, DhBlockPos2D.ZERO /*the quad tree will be re-centered later*/, treeMinDetailLevel); - if (this.minGranularity < LodUtil.CHUNK_DETAIL_LEVEL) { @@ -164,17 +158,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender LodUtil.assertTrue(pos.getDetailLevel() > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); - //if (this.waitingTaskQuadTree.isSectionPosInBounds(requestPos)) - { - CompletableFuture future = new CompletableFuture<>(); - //this.waitingTaskQuadTree.setValue(requestPos, new WorldGenTask(pos, requiredDataDetail, tracker, future)); - waitingTasks.put(pos, new WorldGenTask(pos, requiredDataDetail, tracker, future)); - return future; - } - //else - //{ - //return CompletableFuture.completedFuture(WorldGenResult.CreateFail()); - //} + CompletableFuture future = new CompletableFuture<>(); + this.waitingTasks.put(pos, new WorldGenTask(pos, requiredDataDetail, tracker, future)); + return future; } @Override