minor world gen refactoring

This commit is contained in:
James Seibel
2023-09-28 07:11:24 -05:00
parent 42d80cbe34
commit e6a5a65f21
3 changed files with 105 additions and 123 deletions
@@ -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. <br>
* If false this will return the lowest detail level possible for totally empty sections. <br>
* 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<DhSectionPos> getUngeneratedPosList(byte highestGeneratorDetailLevel, boolean onlyReturnPositionsTheGeneratorCanAccept)
default ArrayList<DhSectionPos> getUngeneratedPosList(byte generatorDetailLevel, boolean onlyReturnPositionsTheGeneratorCanAccept)
{
ArrayList<DhSectionPos> posArray = this.getUngeneratedPosList(this.getSectionPos(), highestGeneratorDetailLevel);
ArrayList<DhSectionPos> posArray = this.getUngeneratedPosListForQuadrant(this.getSectionPos(), generatorDetailLevel);
if (onlyReturnPositionsTheGeneratorCanAccept)
{
LinkedList<DhSectionPos> posList = new LinkedList<>(posArray);
// subdivide positions until they match the generatorDetailLevel
ArrayList<DhSectionPos> 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<DhSectionPos> getUngeneratedPosList(DhSectionPos quadrantPos, byte highestGeneratorDetailLevel)
default ArrayList<DhSectionPos> getUngeneratedPosListForQuadrant(DhSectionPos quadrantPos, byte generatorDetailLevel)
{
ArrayList<DhSectionPos> 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
}
@@ -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;
@@ -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<WorldGenTask> waitingTaskQuadTree;
private final ConcurrentHashMap<DhSectionPos, WorldGenTask> waitingTasks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DhSectionPos, InProgressWorldGenTaskGroup> 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<WorldGenResult> 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<WorldGenResult> future = new CompletableFuture<>();
this.waitingTasks.put(pos, new WorldGenTask(pos, requiredDataDetail, tracker, future));
return future;
}
@Override