diff --git a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataFileHandler.java index 692565b19..230ddb44f 100644 --- a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataFileHandler.java @@ -279,7 +279,8 @@ public class FullDataFileHandler implements IFullDataSourceProvider public void write(DhSectionPos sectionPos, ChunkSizedFullDataSource chunkData) { DhLodPos chunkPos = new DhLodPos((byte) (chunkData.dataDetail+4), chunkData.x, chunkData.z); - LodUtil.assertTrue(chunkPos.overlaps(sectionPos.getSectionBBoxPos()), "Chunk {} does not overlap section {}", chunkPos, sectionPos); + LodUtil.assertTrue(chunkPos.overlaps(sectionPos.getSectionBBoxPos()), "Chunk "+chunkPos+" does not overlap section "+sectionPos); + chunkPos = chunkPos.convertToDetailLevel((byte) this.minDetailLevel); this.recursiveWrite(new DhSectionPos(chunkPos.detailLevel, chunkPos.x, chunkPos.z), chunkData); } diff --git a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/GeneratedFullDataFileHandler.java index 54f66c0c2..1012aaea8 100644 --- a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -5,7 +5,7 @@ import com.seibel.lod.core.dataObjects.fullData.IIncompleteFullDataSource; import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource; 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.AbstractWorldGenTaskTracker; +import com.seibel.lod.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.lod.core.generation.WorldGenerationQueue; import com.seibel.lod.core.level.IDhServerLevel; import com.seibel.lod.core.pos.DhSectionPos; @@ -70,9 +70,9 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler if (worldGenQueue != null) { // queue this section to be generated - GenTask task = new GenTask(pos, new WeakReference<>(dataSource)); - worldGenQueue.submitGenTask(dataSource.getSectionPos().getSectionBBoxPos(), dataSource.getDataDetail(), task) - .whenComplete((genTaskCompleted, ex) -> this.onWorldGenTaskComplete(genTaskCompleted, ex, task, pos)); + GenTask genTask = new GenTask(pos, new WeakReference<>(dataSource)); + worldGenQueue.submitGenTask(dataSource.getSectionPos().getSectionBBoxPos(), dataSource.getDataDetail(), genTask) + .whenComplete((genTaskCompleted, ex) -> this.onWorldGenTaskComplete(genTaskCompleted, ex, genTask, pos)); } // return the empty dataSource (it will be populated later) @@ -117,7 +117,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler - private void onWorldGenTaskComplete(Boolean genTaskCompleted, Throwable exception, GenTask task, DhSectionPos pos) + private void onWorldGenTaskComplete(Boolean genTaskCompleted, Throwable exception, GenTask genTask, DhSectionPos pos) { if (exception != null) { @@ -127,13 +127,13 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler LOGGER.error("Uncaught Gen Task Exception at " + pos + ":", exception); } } - - if (exception == null && genTaskCompleted) + else if (genTaskCompleted) { // this.files.get(task.pos).metaData.dataVersion.incrementAndGet(); return; } - task.releaseStrongReference(); + + genTask.releaseStrongReference(); } @@ -142,47 +142,50 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler // helper class // //==============// - class GenTask extends AbstractWorldGenTaskTracker + private class GenTask implements IWorldGenTaskTracker { private final DhSectionPos pos; - private final WeakReference targetData; - private IFullDataSource loadedTargetData = null; + + // weak reference (probably) used to prevent overloading the GC when lots of gen tasks are created? + private final WeakReference targetFullDataSourceRef; + // the target data source is where the generated chunk data will be put when completed + private IFullDataSource loadedTargetFullDataSource = null; - GenTask(DhSectionPos pos, WeakReference targetData) + public GenTask(DhSectionPos pos, WeakReference targetFullDataSourceRef) { this.pos = pos; - this.targetData = targetData; + this.targetFullDataSourceRef = targetFullDataSourceRef; } @Override - public boolean isMemoryAddressValid() { return this.targetData.get() != null; } + public boolean isMemoryAddressValid() { return this.targetFullDataSourceRef.get() != null; } @Override - public Consumer getConsumer() + public Consumer getOnGenTaskCompleteConsumer() { - if (this.loadedTargetData == null) + if (this.loadedTargetFullDataSource == null) { - this.loadedTargetData = this.targetData.get(); - if (this.loadedTargetData == null) + this.loadedTargetFullDataSource = this.targetFullDataSourceRef.get(); + if (this.loadedTargetFullDataSource == null) { return null; } } - return (chunk) -> + return (chunkSizedFullDataSource) -> { - if (chunk.getBBoxLodPos().overlaps(this.loadedTargetData.getSectionPos().getSectionBBoxPos())) + if (chunkSizedFullDataSource.getBBoxLodPos().overlaps(this.loadedTargetFullDataSource.getSectionPos().getSectionBBoxPos())) { - GeneratedFullDataFileHandler.this.write(this.loadedTargetData.getSectionPos(), chunk); + GeneratedFullDataFileHandler.this.write(this.loadedTargetFullDataSource.getSectionPos(), chunkSizedFullDataSource); } }; } - void releaseStrongReference() { this.loadedTargetData = null; } + public void releaseStrongReference() { this.loadedTargetFullDataSource = null; } } diff --git a/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java index e75c6b57f..e76bac5e4 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java @@ -112,7 +112,7 @@ public class WorldGenerationQueue implements Closeable // task handling // //=================// - public CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, AbstractWorldGenTaskTracker tracker) + public CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { // if the generator is shutting down, don't add new tasks if (this.generatorClosingFuture != null) @@ -146,7 +146,7 @@ public class WorldGenerationQueue implements Closeable DhLodPos cornerSubPos = pos.getCornerLodPos(subDetail); CompletableFuture[] subFutures = new CompletableFuture[subPosWidthCount * subPosWidthCount]; ArrayList subTasks = new ArrayList<>(subPosWidthCount * subPosWidthCount); - SplitTaskTracker splitTaskTracker = new SplitTaskTracker(tracker, new CompletableFuture<>()); + SplitWorldGenTaskTracker splitTaskTracker = new SplitWorldGenTaskTracker(tracker, new CompletableFuture<>()); // create the new sub-futures int subFutureIndex = 0; @@ -241,7 +241,7 @@ public class WorldGenerationQueue implements Closeable { // go through each WorldGenTask in the TaskGroup WorldGenTaskGroup taskGroup = groupIter.next(); - Iterator taskIter = taskGroup.generatorTasks.iterator(); + Iterator taskIter = taskGroup.worldGenTasks.iterator(); while (taskIter.hasNext()) { // remove this task if it has been garbage collected @@ -254,7 +254,7 @@ public class WorldGenerationQueue implements Closeable } // remove this group if it is now empty - if (taskGroup.generatorTasks.isEmpty()) + if (taskGroup.worldGenTasks.isEmpty()) { groupIter.remove(); } @@ -287,7 +287,7 @@ public class WorldGenerationQueue implements Closeable { // the existing group has an equal or lower detail level, // we can just append the new task to its list. - existingWorldGenGroup.generatorTasks.add(task); + existingWorldGenGroup.worldGenTasks.add(task); } else { @@ -300,7 +300,7 @@ public class WorldGenerationQueue implements Closeable LodUtil.assertTrue(taskRemoved); // re-add the task group - existingWorldGenGroup.generatorTasks.add(task); + existingWorldGenGroup.worldGenTasks.add(task); this.addAndCombineTaskGroup(existingWorldGenGroup); } } @@ -317,7 +317,7 @@ public class WorldGenerationQueue implements Closeable if (existingWorldGenGroup != null && existingWorldGenGroup.dataDetail == taskDataDetail) { // We can just append to the higher detail level group - existingWorldGenGroup.generatorTasks.add(task); + existingWorldGenGroup.worldGenTasks.add(task); addedToHigherDetailGroup = true; break; } @@ -328,7 +328,7 @@ public class WorldGenerationQueue implements Closeable // no higher detail group exists that we can append to, // create a new task group existingWorldGenGroup = new WorldGenTaskGroup(task.pos, taskDataDetail); - existingWorldGenGroup.generatorTasks.add(task); + existingWorldGenGroup.worldGenTasks.add(task); this.addAndCombineTaskGroup(existingWorldGenGroup); } } @@ -367,7 +367,7 @@ public class WorldGenerationQueue implements Closeable // We should have already ALWAYS selected the higher granularity. LodUtil.assertTrue(group.pos.detailLevel < newTaskGroup.pos.detailLevel); groupIter.remove(); // Remove and consume all from that lower granularity request - newTaskGroup.generatorTasks.addAll(group.generatorTasks); + newTaskGroup.worldGenTasks.addAll(group.worldGenTasks); } } @@ -419,7 +419,7 @@ public class WorldGenerationQueue implements Closeable // We can just append us into the existing list. for (WorldGenTaskGroup g : groups) { - newGroup.generatorTasks.addAll(g.generatorTasks); + newGroup.worldGenTasks.addAll(g.worldGenTasks); } } else @@ -430,7 +430,7 @@ public class WorldGenerationQueue implements Closeable LodUtil.assertTrue(worked); for (WorldGenTaskGroup g : groups) { - newGroup.generatorTasks.addAll(g.generatorTasks); + newGroup.worldGenTasks.addAll(g.worldGenTasks); } this.addAndCombineTaskGroup(newGroup); // Recursive check the new group } @@ -441,7 +441,7 @@ public class WorldGenerationQueue implements Closeable newGroup = new WorldGenTaskGroup(parentPos, newTaskGroup.dataDetail); for (WorldGenTaskGroup g : groups) { - newGroup.generatorTasks.addAll(g.generatorTasks); + newGroup.worldGenTasks.addAll(g.worldGenTasks); } this.addAndCombineTaskGroup(newGroup); // Recursive check the new group } @@ -559,7 +559,8 @@ public class WorldGenerationQueue implements Closeable DhChunkPos chunkPosMin = new DhChunkPos(pos.getCornerBlockPos()); //LOGGER.info("Generating section {} with granularity {} at {}", pos, granularity, chunkPosMin); - task.genFuture = startGenerationEvent(this.generator, chunkPosMin, granularity, dataDetail, task.group::accept); + + task.genFuture = startGenerationEvent(this.generator, chunkPosMin, granularity, dataDetail, task.group::onGenerationComplete); task.genFuture.whenComplete((voidObj, exception) -> { if (exception != null) @@ -570,12 +571,12 @@ public class WorldGenerationQueue implements Closeable LOGGER.error("Error generating data for section "+pos, exception); } - task.group.generatorTasks.forEach(worldGenTask -> worldGenTask.future.complete(false)); + task.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(false)); } else { //LOGGER.info("Section generation at "+pos+" completed"); - task.group.generatorTasks.forEach(worldGenTask -> worldGenTask.future.complete(true)); + task.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(true)); } boolean worked = this.inProgressGenTasksByLodPos.remove(pos, task); LodUtil.assertTrue(worked); @@ -612,7 +613,7 @@ public class WorldGenerationQueue implements Closeable { // go through each WorldGenTask in the TaskGroup WorldGenTaskGroup taskGroup = taskGroupIter.next(); - Iterator taskIter = taskGroup.generatorTasks.iterator(); + Iterator taskIter = taskGroup.worldGenTasks.iterator(); while (taskIter.hasNext()) { // remove this task if it has been garbage collected @@ -629,7 +630,7 @@ public class WorldGenerationQueue implements Closeable } // remove this group if it is now empty - if (taskGroup.generatorTasks.isEmpty()) + if (taskGroup.worldGenTasks.isEmpty()) { taskGroupIter.remove(); } @@ -650,7 +651,7 @@ public class WorldGenerationQueue implements Closeable public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { - this.waitingTaskGroupsByLodPos.values().forEach(worldGenTaskGroup -> worldGenTaskGroup.generatorTasks.forEach( + this.waitingTaskGroupsByLodPos.values().forEach(worldGenTaskGroup -> worldGenTaskGroup.worldGenTasks.forEach( (worldGenTask) -> { try { worldGenTask.future.cancel(true); } catch (CancellationException ignored) { /* don't log shutdown exceptions */ } @@ -754,7 +755,7 @@ public class WorldGenerationQueue implements Closeable private static CompletableFuture startGenerationEvent(IDhApiWorldGenerator worldGenerator, DhChunkPos chunkPosMin, byte granularity, byte targetDataDetail, - Consumer resultConsumer) + Consumer generationCompleteConsumer) { EDhApiDistantGeneratorMode generatorMode = Config.Client.WorldGenerator.distantGeneratorMode.get(); return worldGenerator.generateChunks(chunkPosMin.x, chunkPosMin.z, granularity, targetDataDetail, generatorMode, (objectArray) -> @@ -762,7 +763,7 @@ public class WorldGenerationQueue implements Closeable try { IChunkWrapper chunk = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createChunkWrapper(objectArray); - resultConsumer.accept(LodDataBuilder.createChunkData(chunk)); + generationCompleteConsumer.accept(LodDataBuilder.createChunkData(chunk)); } catch (ClassCastException e) { diff --git a/core/src/main/java/com/seibel/lod/core/generation/tasks/AbstractWorldGenTaskTracker.java b/core/src/main/java/com/seibel/lod/core/generation/tasks/AbstractWorldGenTaskTracker.java deleted file mode 100644 index 1bb864a73..000000000 --- a/core/src/main/java/com/seibel/lod/core/generation/tasks/AbstractWorldGenTaskTracker.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.lod.core.generation.tasks; - -import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource; - -import java.util.function.Consumer; - -/** - * @author Leetom - * @version 2022-11-25 - */ -public abstract class AbstractWorldGenTaskTracker -{ - /** - * Returns true if the task hasn't been garbage collected.
- * TODO rename to fit the above description better - */ - public abstract boolean isMemoryAddressValid(); - - public abstract Consumer getConsumer(); - -} diff --git a/core/src/main/java/com/seibel/lod/core/generation/tasks/IWorldGenTaskTracker.java b/core/src/main/java/com/seibel/lod/core/generation/tasks/IWorldGenTaskTracker.java new file mode 100644 index 000000000..09422e52d --- /dev/null +++ b/core/src/main/java/com/seibel/lod/core/generation/tasks/IWorldGenTaskTracker.java @@ -0,0 +1,18 @@ +package com.seibel.lod.core.generation.tasks; + +import com.seibel.lod.core.dataObjects.fullData.sources.ChunkSizedFullDataSource; + +import java.util.function.Consumer; + +/** + * @author Leetom + * @version 2022-11-25 + */ +public interface IWorldGenTaskTracker +{ + /** Returns true if the task hasn't been garbage collected. */ + boolean isMemoryAddressValid(); + + Consumer getOnGenTaskCompleteConsumer(); + +} diff --git a/core/src/main/java/com/seibel/lod/core/generation/tasks/SplitTaskTracker.java b/core/src/main/java/com/seibel/lod/core/generation/tasks/SplitWorldGenTaskTracker.java similarity index 69% rename from core/src/main/java/com/seibel/lod/core/generation/tasks/SplitTaskTracker.java rename to core/src/main/java/com/seibel/lod/core/generation/tasks/SplitWorldGenTaskTracker.java index 6cedacad9..e3fb263b2 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/tasks/SplitTaskTracker.java +++ b/core/src/main/java/com/seibel/lod/core/generation/tasks/SplitWorldGenTaskTracker.java @@ -13,9 +13,9 @@ import com.seibel.lod.core.generation.WorldGenerationQueue; * @author Leetom * @version 2022-11-25 */ -public class SplitTaskTracker extends AbstractWorldGenTaskTracker +public class SplitWorldGenTaskTracker implements IWorldGenTaskTracker { - public final AbstractWorldGenTaskTracker parentTracker; + public final IWorldGenTaskTracker parentTracker; public final CompletableFuture parentFuture; /** cached value to allow for quicker checking */ @@ -23,7 +23,7 @@ public class SplitTaskTracker extends AbstractWorldGenTaskTracker - public SplitTaskTracker(AbstractWorldGenTaskTracker parentTracker, CompletableFuture parentFuture) + public SplitWorldGenTaskTracker(IWorldGenTaskTracker parentTracker, CompletableFuture parentFuture) { this.parentTracker = parentTracker; this.parentFuture = parentFuture; @@ -31,7 +31,7 @@ public class SplitTaskTracker extends AbstractWorldGenTaskTracker - /** Recalculates and returns the new {@link SplitTaskTracker#isValid} value */ + /** Recalculates and returns the new {@link SplitWorldGenTaskTracker#isValid} value */ public boolean recalculateIsValid() { if (!this.isValid) @@ -52,6 +52,6 @@ public class SplitTaskTracker extends AbstractWorldGenTaskTracker public boolean isMemoryAddressValid() { return this.isValid; } @Override - public Consumer getConsumer() { return this.parentTracker.getConsumer(); } + public Consumer getOnGenTaskCompleteConsumer() { return this.parentTracker.getOnGenTaskCompleteConsumer(); } } diff --git a/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTask.java b/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTask.java index d0b710755..be2de0637 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTask.java +++ b/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTask.java @@ -12,11 +12,11 @@ public final class WorldGenTask { public final DhLodPos pos; public final byte dataDetailLevel; - public final AbstractWorldGenTaskTracker taskTracker; + public final IWorldGenTaskTracker taskTracker; public final CompletableFuture future; - public WorldGenTask(DhLodPos pos, byte dataDetail, AbstractWorldGenTaskTracker taskTracker, CompletableFuture future) + public WorldGenTask(DhLodPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture future) { this.dataDetailLevel = dataDetail; this.pos = pos; diff --git a/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTaskGroup.java b/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTaskGroup.java index 481017f42..4a045aa02 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTaskGroup.java +++ b/core/src/main/java/com/seibel/lod/core/generation/tasks/WorldGenTaskGroup.java @@ -16,7 +16,7 @@ public final class WorldGenTaskGroup public final DhLodPos pos; public byte dataDetail; /** Only accessed by the generator polling thread */ - public final LinkedList generatorTasks = new LinkedList<>(); + public final LinkedList worldGenTasks = new LinkedList<>(); @@ -28,21 +28,22 @@ public final class WorldGenTaskGroup - public void accept(ChunkSizedFullDataSource data) + public void onGenerationComplete(ChunkSizedFullDataSource chunkSizedFullDataSource) { - Iterator tasks = this.generatorTasks.iterator(); + Iterator tasks = this.worldGenTasks.iterator(); while (tasks.hasNext()) { WorldGenTask task = tasks.next(); - Consumer consumer = task.taskTracker.getConsumer(); - if (consumer == null) + Consumer onGenTaskCompleteConsumer = task.taskTracker.getOnGenTaskCompleteConsumer(); + if (onGenTaskCompleteConsumer == null) { tasks.remove(); task.future.complete(false); } else { - consumer.accept(data); + // TODO why aren't we removing the task if it has a consumer? + onGenTaskCompleteConsumer.accept(chunkSizedFullDataSource); } } }