finish the world generatior
This commit is contained in:
+17
-4
@@ -7,6 +7,7 @@ import com.seibel.lod.core.dataObjects.fullData.sources.SparseFullDataSource;
|
||||
import com.seibel.lod.core.dataObjects.fullData.sources.SingleChunkFullDataSource;
|
||||
import com.seibel.lod.core.generation.tasks.IWorldGenTaskTracker;
|
||||
import com.seibel.lod.core.generation.WorldGenerationQueue;
|
||||
import com.seibel.lod.core.generation.tasks.WorldGenResult;
|
||||
import com.seibel.lod.core.level.IDhServerLevel;
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
@@ -105,7 +106,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
|
||||
// queue this section to be generated
|
||||
GenTask genTask = new GenTask(pos, new WeakReference<>(incompleteFullDataSource));
|
||||
worldGenQueue.submitGenTask(incompleteFullDataSource.getSectionPos().getSectionBBoxPos(), incompleteFullDataSource.getDataDetail(), genTask)
|
||||
.whenComplete((genTaskCompleted, ex) -> this.onWorldGenTaskComplete(genTaskCompleted, ex, genTask, pos));
|
||||
.whenComplete((genTaskResult, ex) -> this.onWorldGenTaskComplete(genTaskResult, ex, genTask, pos));
|
||||
}
|
||||
|
||||
// return the empty dataSource (it will be populated later)
|
||||
@@ -149,18 +150,20 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
|
||||
}
|
||||
}
|
||||
|
||||
private void onWorldGenTaskComplete(Boolean genTaskCompleted, Throwable exception, GenTask genTask, DhSectionPos pos)
|
||||
private void onWorldGenTaskComplete(WorldGenResult genTaskResult, Throwable exception, GenTask genTask, DhSectionPos pos)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
// don't log the shutdown exceptions
|
||||
// don't log shutdown exceptions
|
||||
if (!(exception instanceof CancellationException || exception.getCause() instanceof CancellationException))
|
||||
{
|
||||
LOGGER.error("Uncaught Gen Task Exception at " + pos + ":", exception);
|
||||
}
|
||||
}
|
||||
else if (genTaskCompleted)
|
||||
else if (genTaskResult.success)
|
||||
{
|
||||
// generation completed, update the files and listener(s)
|
||||
|
||||
this.files.get(genTask.pos).flushAndSave();
|
||||
|
||||
// fire the event listeners
|
||||
@@ -172,6 +175,16 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
|
||||
// this.files.get(genTask.pos).metaData.dataVersion.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// generation didn't complete
|
||||
|
||||
// if the generation task was split up into smaller positions, wait for them to complete
|
||||
for (CompletableFuture<WorldGenResult> siblingFuture : genTaskResult.childFutures)
|
||||
{
|
||||
siblingFuture.whenComplete((siblingGenTaskResult, siblingEx) -> this.onWorldGenTaskComplete(siblingGenTaskResult, siblingEx, genTask, pos));
|
||||
}
|
||||
}
|
||||
|
||||
genTask.releaseStrongReference();
|
||||
}
|
||||
|
||||
@@ -75,23 +75,16 @@ public class WorldGenerationQueue implements Closeable
|
||||
// task handling //
|
||||
//=================//
|
||||
|
||||
public CompletableFuture<Boolean> submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
|
||||
public CompletableFuture<WorldGenResult> submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
|
||||
{
|
||||
// TODO implement multiple detail level generation
|
||||
if (pos.detailLevel != 6)
|
||||
// if (!(pos.detailLevel >= this.minGranularity && pos.detailLevel <= this.maxGranularity))
|
||||
{
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
|
||||
|
||||
// the generator is shutting down, don't add new tasks
|
||||
if (this.generatorClosingFuture != null)
|
||||
{
|
||||
return CompletableFuture.completedFuture(false);
|
||||
return CompletableFuture.completedFuture(WorldGenResult.CreateFail());
|
||||
}
|
||||
|
||||
// TODO what does these checks and the assert below mean?
|
||||
|
||||
// make sure the generator can provide the requested position
|
||||
if (requiredDataDetail < this.minDataDetail)
|
||||
{
|
||||
throw new UnsupportedOperationException("Current generator does not meet requiredDataDetail level");
|
||||
@@ -101,11 +94,12 @@ public class WorldGenerationQueue implements Closeable
|
||||
requiredDataDetail = this.maxDataDetail;
|
||||
}
|
||||
|
||||
// TODO what does this assert mean?
|
||||
LodUtil.assertTrue(pos.detailLevel > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL/*TODO is chunkDetailLevel the correct replacement? otherwise the magic number was 4*/);
|
||||
|
||||
|
||||
|
||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||
CompletableFuture<WorldGenResult> future = new CompletableFuture<>();
|
||||
this.waitingTaskQuadTree.set(new DhSectionPos(pos.detailLevel, pos.x, pos.z), new WorldGenTask(pos, requiredDataDetail, tracker, future));
|
||||
return future;
|
||||
}
|
||||
@@ -174,13 +168,16 @@ public class WorldGenerationQueue implements Closeable
|
||||
if (genTask != null && !genTask.taskTracker.isMemoryAddressValid())
|
||||
{
|
||||
taskIterator.remove();
|
||||
genTask.future.complete(false);
|
||||
genTask.future.complete(WorldGenResult.CreateFail());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param targetPos the position to center the generation around */
|
||||
/**
|
||||
* @param targetPos the position to center the generation around
|
||||
* @return false if no tasks were found to generate
|
||||
*/
|
||||
private boolean startNextWorldGenTask(DhBlockPos2D targetPos)
|
||||
{
|
||||
WorldGenTask closestTask = null;
|
||||
@@ -259,35 +256,46 @@ public class WorldGenerationQueue implements Closeable
|
||||
}
|
||||
else
|
||||
{
|
||||
// detail level is (probably) too high, split up the task
|
||||
LodUtil.assertTrue(closestTask == removedWorldGenTask); // should be the same memory address, removedWorldGenTask shouldn't be null // TODO why shouldn't it be null?
|
||||
// detail level is too high (if the detail level was too low, the generator would've ignored the request),
|
||||
// split up the task
|
||||
|
||||
// make sure that we have a task to split up
|
||||
LodUtil.assertTrue(closestTask == removedWorldGenTask);
|
||||
|
||||
|
||||
// split up the task and add each one to the tree
|
||||
LinkedList<CompletableFuture<WorldGenResult>> childFutures = new LinkedList<>();
|
||||
DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z);
|
||||
sectionPos.forEachChild((childDhSectionPos) ->
|
||||
{
|
||||
WorldGenTask newGenTask = new WorldGenTask(new DhLodPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), childDhSectionPos.sectionDetailLevel, removedWorldGenTask.taskTracker, removedWorldGenTask.future /*TODO probably need to do something about the futures here*/);
|
||||
CompletableFuture<WorldGenResult> newFuture = new CompletableFuture<>();
|
||||
childFutures.add(newFuture);
|
||||
|
||||
WorldGenTask newGenTask = new WorldGenTask(new DhLodPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), childDhSectionPos.sectionDetailLevel, removedWorldGenTask.taskTracker, newFuture);
|
||||
this.waitingTaskQuadTree.set(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ, newGenTask);
|
||||
});
|
||||
|
||||
// send the child futures to the future recipient, to notify them of the new tasks
|
||||
removedWorldGenTask.future.complete(WorldGenResult.CreateSplit(childFutures));
|
||||
|
||||
|
||||
// return true so we attempt to generate again
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private void startWorldGenTaskGroup(InProgressWorldGenTaskGroup task)
|
||||
private void startWorldGenTaskGroup(InProgressWorldGenTaskGroup inProgressTaskGroup)
|
||||
{
|
||||
byte taskDetailLevel = task.group.dataDetail;
|
||||
DhLodPos taskPos = task.group.pos;
|
||||
byte taskDetailLevel = inProgressTaskGroup.group.dataDetail;
|
||||
DhLodPos taskPos = inProgressTaskGroup.group.pos;
|
||||
byte granularity = (byte) (taskPos.detailLevel - taskDetailLevel);
|
||||
LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity);
|
||||
LodUtil.assertTrue(taskDetailLevel >= this.minDataDetail && taskDetailLevel <= this.maxDataDetail);
|
||||
|
||||
DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getCornerBlockPos());
|
||||
LOGGER.info("Generating section "+taskPos+" with granularity "+granularity+" at "+chunkPosMin);
|
||||
// LOGGER.info("Generating section "+taskPos+" with granularity "+granularity+" at "+chunkPosMin);
|
||||
|
||||
task.genFuture = startGenerationEvent(this.generator, chunkPosMin, granularity, taskDetailLevel, task.group::onGenerationComplete);
|
||||
task.genFuture.whenComplete((voidObj, exception) ->
|
||||
inProgressTaskGroup.genFuture = startGenerationEvent(this.generator, chunkPosMin, granularity, taskDetailLevel, inProgressTaskGroup.group::onGenerationComplete);
|
||||
inProgressTaskGroup.genFuture.whenComplete((voidObj, exception) ->
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
@@ -297,14 +305,14 @@ public class WorldGenerationQueue implements Closeable
|
||||
LOGGER.error("Error generating data for section "+taskPos, exception);
|
||||
}
|
||||
|
||||
task.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(false));
|
||||
inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateFail()));
|
||||
}
|
||||
else
|
||||
{
|
||||
//LOGGER.info("Section generation at "+pos+" completed");
|
||||
task.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(true));
|
||||
inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos))));
|
||||
}
|
||||
boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, task);
|
||||
boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, inProgressTaskGroup);
|
||||
LodUtil.assertTrue(worked);
|
||||
});
|
||||
}
|
||||
@@ -317,25 +325,9 @@ public class WorldGenerationQueue implements Closeable
|
||||
|
||||
public CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
|
||||
{
|
||||
// remove any incomplete generation tasks
|
||||
for (byte detailLevel = QuadTree.TREE_LOWEST_DETAIL_LEVEL; detailLevel < this.waitingTaskQuadTree.treeMaxDetailLevel; detailLevel++)
|
||||
{
|
||||
// TODO remove
|
||||
// Iterator<WorldGenTask> ringListIterator = this.waitingTaskQuadTree.getRingList(detailLevel).iterator();
|
||||
// while (ringListIterator.hasNext())
|
||||
// {
|
||||
// WorldGenTask worldGenTask = ringListIterator.next();
|
||||
// if (worldGenTask != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// worldGenTask.future.cancel(true);
|
||||
// }
|
||||
// catch (CancellationException ignored)
|
||||
// { /* don't log shutdown exceptions */ }
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO shouldn't I clear the list? not just cancel each item?
|
||||
MovableGridRingList<WorldGenTask> ringList = this.waitingTaskQuadTree.getRingList(detailLevel);
|
||||
ringList.clear((worldGenTask) ->
|
||||
{
|
||||
@@ -352,6 +344,7 @@ public class WorldGenerationQueue implements Closeable
|
||||
}
|
||||
|
||||
|
||||
// stop and remove any in progress tasks
|
||||
ArrayList<CompletableFuture<Void>> inProgressTasksCancelingFutures = new ArrayList<>(this.inProgressGenTasksByLodPos.size());
|
||||
this.inProgressGenTasksByLodPos.values().forEach(runningTaskGroup ->
|
||||
{
|
||||
@@ -377,7 +370,7 @@ public class WorldGenerationQueue implements Closeable
|
||||
return null;
|
||||
}));
|
||||
});
|
||||
this.generatorClosingFuture = CompletableFuture.allOf(inProgressTasksCancelingFutures.toArray(new CompletableFuture[0])); //FIXME: Closer threading issues with runCurrentGenTasksUntilBusy
|
||||
this.generatorClosingFuture = CompletableFuture.allOf(inProgressTasksCancelingFutures.toArray(new CompletableFuture[0]));
|
||||
|
||||
return this.generatorClosingFuture;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.seibel.lod.core.generation.tasks;
|
||||
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
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;
|
||||
/** 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<CompletableFuture<WorldGenResult>> childFutures = new LinkedList<>();
|
||||
|
||||
|
||||
public static WorldGenResult CreateSplit(Collection<CompletableFuture<WorldGenResult>> 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<CompletableFuture<WorldGenResult>> childFutures)
|
||||
{
|
||||
this.success = success;
|
||||
this.pos = pos;
|
||||
|
||||
if (childFutures != null)
|
||||
{
|
||||
this.childFutures.addAll(childFutures);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.seibel.lod.core.generation.tasks;
|
||||
|
||||
import com.seibel.lod.core.pos.DhLodPos;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -13,10 +14,11 @@ public final class WorldGenTask
|
||||
public final DhLodPos pos;
|
||||
public final byte dataDetailLevel;
|
||||
public final IWorldGenTaskTracker taskTracker;
|
||||
public final CompletableFuture<Boolean> future;
|
||||
public final CompletableFuture<WorldGenResult> future;
|
||||
|
||||
|
||||
public WorldGenTask(DhLodPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<Boolean> future)
|
||||
|
||||
public WorldGenTask(DhLodPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<WorldGenResult> future)
|
||||
{
|
||||
this.dataDetailLevel = dataDetail;
|
||||
this.pos = pos;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.seibel.lod.core.generation.tasks;
|
||||
|
||||
import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource;
|
||||
import com.seibel.lod.core.pos.DhLodPos;
|
||||
import com.seibel.lod.core.pos.DhSectionPos;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -38,7 +39,7 @@ public final class WorldGenTaskGroup
|
||||
if (onGenTaskCompleteConsumer == null)
|
||||
{
|
||||
tasks.remove();
|
||||
task.future.complete(false);
|
||||
task.future.complete(WorldGenResult.CreateFail());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -66,6 +66,13 @@ public class DhSectionPos
|
||||
this.sectionZ = lodPos.z;
|
||||
}
|
||||
|
||||
public DhSectionPos(byte detailLevel, DhLodPos dhLodPos)
|
||||
{
|
||||
this.sectionDetailLevel = detailLevel;
|
||||
this.sectionX = dhLodPos.x;
|
||||
this.sectionZ = dhLodPos.z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Returns the center for the highest detail level (0) */
|
||||
|
||||
@@ -80,6 +80,13 @@ public class LodRenderSection
|
||||
|
||||
public void reload(ILodRenderSourceProvider renderDataProvider)
|
||||
{
|
||||
// don't accidentally enable rendering for a disabled section
|
||||
if (!this.isRenderEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.renderSourceProvider = renderDataProvider;
|
||||
|
||||
if (this.loadFuture != null)
|
||||
|
||||
@@ -236,6 +236,19 @@ public class QuadTree<T>
|
||||
}
|
||||
public boolean isDetailLevelEmpty(byte detailLevel) { return this.getRingList(detailLevel).isEmpty(); }
|
||||
|
||||
/** returns the number of items in this QuadTree */
|
||||
public int size()
|
||||
{
|
||||
int size = 0;
|
||||
for (byte detailLevel = QuadTree.TREE_LOWEST_DETAIL_LEVEL; detailLevel < this.treeMaxDetailLevel; detailLevel++)
|
||||
{
|
||||
size += getRingList(detailLevel).size();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
public String getDebugString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Reference in New Issue
Block a user