diff --git a/api/src/main/java/com/seibel/lod/api/enums/EDhApiDetailLevel.java b/api/src/main/java/com/seibel/lod/api/enums/EDhApiDetailLevel.java index b6e0ce518..fe7daa764 100644 --- a/api/src/main/java/com/seibel/lod/api/enums/EDhApiDetailLevel.java +++ b/api/src/main/java/com/seibel/lod/api/enums/EDhApiDetailLevel.java @@ -40,13 +40,13 @@ public enum EDhApiDetailLevel REGION(9, 512); - public final int detailLevel; - public final int widthInBlocks; + public final byte detailLevel; + public final byte widthInBlocks; EDhApiDetailLevel(int detailLevel, int widthInBlocks) { - this.detailLevel = detailLevel; - this.widthInBlocks = widthInBlocks; + this.detailLevel = (byte) detailLevel; + this.widthInBlocks = (byte) widthInBlocks; } } diff --git a/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java index ff813a3dd..f527c82de 100644 --- a/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java +++ b/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java @@ -1,20 +1,125 @@ package com.seibel.lod.api.interfaces.override.worldGenerator; +import com.seibel.lod.api.enums.EDhApiDetailLevel; import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenThreadMode; -import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.lod.api.interfaces.override.IDhApiOverrideable; -import com.seibel.lod.api.interfaces.world.IDhApiChunkWrapper; -import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; + +import java.io.Closeable; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; /** * @author James Seibel - * @version 2022-9-8 + * @version 2022-12-6 */ -public interface IDhApiWorldGenerator extends IDhApiOverrideable +public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable { - /** Returns which thread chunk generation requests can be created on. */ + //============// + // parameters // + //============// + + /** + * Returns which thread chunk generation requests will be run on.
+ * TODO: only {@link EDhApiWorldGenThreadMode#MULTI_THREADED} is supported currently + */ EDhApiWorldGenThreadMode getThreadingMode(); - IDhApiChunkWrapper generateCoreChunk(int chunkPosX, int chunkPosZ, IDhApiLevelWrapper serverLevelWrapper, EDhApiWorldGenerationStep maxStepToGenerate); + /** + * Defines the smallest datapoint size that can be generated at a time.
+ * Minimum detail level is 0 (1 block)
+ * Default detail level is 0
+ * For more information on what detail levels represent see: {@link EDhApiDetailLevel}.

+ * + * TODO: System currently only supports 1x1 block per data. + * + * @see EDhApiDetailLevel + */ + default byte getMinDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; } + /** + * Defines the largest datapoint size that can be generated at a time.
+ * Minimum detail level is 0 (1 block)
+ * Default detail level is 0
+ * For more information on what detail levels represent see: {@link EDhApiDetailLevel}. + * + * @see EDhApiDetailLevel + */ + default byte getMaxDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; } + + /** + * When creating generation requests the system will attempt to group nearby tasks together.

+ * What is the minimum size a single generation call can batch together?
+ * + * Minimum detail level is 4 (the size of a MC chunk)
+ * Default detail level is 4
+ * For more information on what detail levels represent see: {@link EDhApiDetailLevel}. + * + * @see EDhApiDetailLevel + */ + default byte getMinGenerationGranularity() { return EDhApiDetailLevel.CHUNK.detailLevel; } + + /** + * When creating generation requests the system will attempt to group nearby tasks together.

+ * What is the maximum size a single generation call can batch together?
+ * + * Minimum detail level is 4 (the size of a MC chunk)
+ * Default detail level is 6 (4x4 chunks)
+ * For more information on what detail levels represent see: {@link EDhApiDetailLevel}. + * + * @see EDhApiDetailLevel + */ + default byte getMaxGenerationGranularity() { return (byte) (EDhApiDetailLevel.CHUNK.detailLevel + 2); } + + /** Returns true if the generator is unable to accept new generation requests. */ + boolean isBusy(); + + + + + //=================// + // world generator // + //=================// + + /** + * This method is called by Distant Horizons to generate terrain over a given area + * from a thread defined by {@link IDhApiWorldGenerator#getThreadingMode}.

+ * + * After a chunk has been generated it (and any necessary supporting objects as listed below) should be passed into the + * resultConsumer's {@link Consumer#accept} method. If the Consumer is given the wrong data + * type(s) it will throw a {@link ClassCastException} with a list of what objects it was expecting.
+ * Note: these objects are minecraft version dependent and will change without notice! + * Please run your generator in game at least once to confirm the objects you are returning are correct.

+ * + * Consumer expected inputs for each minecraft version (in order):
+ * 1.18: {@link net.minecraft.world.level.chunk.ChunkAccess} and {@link net.minecraft.world.level.LevelReader}
+ * + * @throws ClassCastException if incompatible objects are passed into the resultConsumer. + */ + CompletableFuture generateChunks(int chunkPosMinX, int chunkPosMinZ, + byte granularity, byte targetDataDetail, + Consumer resultConsumer) throws ClassCastException; + + + + //===============// + // event methods // + //===============// + + /** + * Called before a new generator task is started.
+ * This can be used to run cleanup on existing tasks before new tasks are started. + */ + void preGeneratorTaskStart(); + + + + //===========// + // overrides // + //===========// + + // This is overridden to remove the "throws IOException" + // that is present in the default Closeable.close() method + @Override + void close(); + } diff --git a/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java b/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java index 8c4f2c385..1678ee655 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java +++ b/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java @@ -19,6 +19,7 @@ package com.seibel.lod.core.generation; +import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenThreadMode; import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.config.Config; import com.seibel.lod.api.enums.config.EDistanceGenerationMode; @@ -65,15 +66,16 @@ public class BatchGenerator implements IChunkGenerator // generator parameters // //======================// + @Override + public EDhApiWorldGenThreadMode getThreadingMode() { return EDhApiWorldGenThreadMode.MULTI_THREADED; } + @Override public byte getMinDataDetailLevel() { return LodUtil.BLOCK_DETAIL_LEVEL; } - @Override public byte getMaxDataDetailLevel() { return LodUtil.BLOCK_DETAIL_LEVEL; } @Override public byte getMinGenerationGranularity() { return LodUtil.CHUNK_DETAIL_LEVEL; } - @Override public byte getMaxGenerationGranularity() { return LodUtil.CHUNK_DETAIL_LEVEL + 2; } @@ -99,7 +101,7 @@ public class BatchGenerator implements IChunkGenerator } @Override - public CompletableFuture generateChunks(DhChunkPos chunkPosMin, byte granularity, byte targetDataDetail, Consumer resultConsumer) + public CompletableFuture generateChunks(int chunkPosMinX, int chunkPosMinZ, byte granularity, byte targetDataDetail, Consumer resultConsumer) { EDistanceGenerationMode mode = Config.Client.WorldGenerator.distanceGenerationMode.get(); Steps targetStep = null; @@ -123,13 +125,15 @@ public class BatchGenerator implements IChunkGenerator break; } - int chunkXMin = chunkPosMin.x; - int chunkZMin = chunkPosMin.z; int genChunkSize = BitShiftUtil.powerOfTwo(granularity - 4); // minus 4 is equal to dividing by 16 to convert to chunk scale double runTimeRatio = Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get() > 1 ? 1.0 : Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get(); - return this.generationGroup.generateChunks(chunkXMin, chunkZMin, genChunkSize, targetStep, runTimeRatio, resultConsumer); + + // the consumer needs to be wrapped like this because the API can't use DH core objects (and IChunkWrapper can't be easily put into the API project) + Consumer consumer = (chunkWrapper) -> resultConsumer.accept(new Object[]{ chunkWrapper }); + + return this.generationGroup.generateChunks(chunkPosMinX, chunkPosMinZ, genChunkSize, targetStep, runTimeRatio, consumer); } @Override diff --git a/core/src/main/java/com/seibel/lod/core/generation/IChunkGenerator.java b/core/src/main/java/com/seibel/lod/core/generation/IChunkGenerator.java index edb5d9a94..acbf11af7 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/IChunkGenerator.java +++ b/core/src/main/java/com/seibel/lod/core/generation/IChunkGenerator.java @@ -1,8 +1,13 @@ package com.seibel.lod.core.generation; +import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; +import com.seibel.lod.core.config.Config; import com.seibel.lod.core.datatype.full.ChunkSizedData; import com.seibel.lod.core.datatype.transform.LodDataBuilder; +import com.seibel.lod.core.dependencyInjection.SingletonInjector; +import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.pos.DhChunkPos; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import java.io.Closeable; @@ -10,53 +15,15 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; /** + * Most logic is in {@link IDhApiWorldGenerator}, this mainly contains default + * methods that are useful for Core. + * + * @author James Seibel * @author Leetom - * @version 2022-11-25 + * @version 2022-12-5 */ -public interface IChunkGenerator extends Closeable +public interface IChunkGenerator extends IDhApiWorldGenerator { - //============// - // parameters // - //============// - - /** - * What is the detail/resolution of the data? (This will offset the generation granularity) - * (minimum detail is 0, maximum detail is 255) (though that high isn't really... realistic) - * (0 = 1x1 block per data, 1 = 2x2 block per data, 2 = 4x4 block per data, etc. This measured in the same units as LOD Detail Level.) - * TODO: System currently only supports 1x1 block per data. - */ - byte getMinDataDetailLevel(); - byte getMaxDataDetailLevel(); - - /** - * What is the min batch size of a single generation call? - * (minimum return value is 4 since that's the MC chunk size) - * (4 -> 16x16 data per call, 5 -> 32x32 data per call, 6 -> 64x64 data per call, etc. This measured in the same units as LOD Detail Level.) - */ - byte getMinGenerationGranularity(); - - /** - * What is the max batch size of a single generation call? - * The system will try to group tasks to the max batch size if possible - * (minimum return value is 4 since that's the MC chunk size) - * (4 -> 16x16 data per call, 5 -> 32x32 data per call, 6 -> 64x64 data per call, etc. This measured in the same units as LOD Detail Level.) - */ - byte getMaxGenerationGranularity(); - - /** Returns whether the generator is unable to accept new generation requests. */ - boolean isBusy(); - - - - //=================// - // world generator // - //=================// - - CompletableFuture generateChunks(DhChunkPos chunkPosMin, - byte granularity, byte targetDataDetail, - Consumer resultConsumer); - - /** * Start a generation event * (Note that the chunkPos is always aligned to the granularity) @@ -66,32 +33,19 @@ public interface IChunkGenerator extends Closeable byte granularity, byte targetDataDetail, Consumer resultConsumer) { - return this.generateChunks(chunkPosMin, granularity, targetDataDetail, (chunk) -> - { - resultConsumer.accept(LodDataBuilder.createChunkData(chunk)); + return this.generateChunks(chunkPosMin.x, chunkPosMin.z, granularity, targetDataDetail, (objectArray) -> + { + try + { + IChunkWrapper chunk = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createChunkWrapper(objectArray); + resultConsumer.accept(LodDataBuilder.createChunkData(chunk)); + } + catch (ClassCastException e) + { + DhLoggerBuilder.getLogger().error("World generator return type incorrect. Error: [" + e.getMessage() + "].", e); + Config.Client.WorldGenerator.enableDistantGeneration.set(false); + } }); } - - //===============// - // event methods // - //===============// - - /** - * Called before a new generator task is started.
- * This can be used to run cleanup on existing tasks before new tasks are started. - */ - void preGeneratorTaskStart(); - - - - //===========// - // overrides // - //===========// - - // This is overridden to remove the "throws IOException" - // that is present in the default Closeable.close() method - @Override - void close(); - } diff --git a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java index d0e627e8b..7d1b1f130 100644 --- a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java @@ -22,6 +22,7 @@ package com.seibel.lod.core.wrapperInterfaces; import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.interfaces.dependencyInjection.IBindable; import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; @@ -31,7 +32,7 @@ import java.io.IOException; * This handles creating abstract wrapper objects. * * @author James Seibel - * @version 3-5-2022 + * @version 2022-12-5 */ public interface IWrapperFactory extends IBindable { @@ -39,4 +40,11 @@ public interface IWrapperFactory extends IBindable IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); + + /** + * Specifically designed to be used with the API. + * @throws ClassCastException with instructions on expected objects if the object couldn't be cast + */ + IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException; + } diff --git a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java index 24a9e5d3f..bbd033480 100644 --- a/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java +++ b/core/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java @@ -43,4 +43,5 @@ public abstract class AbstractBatchGenerationEnvionmentWrapper public abstract void stop(boolean blocking); public abstract CompletableFuture generateChunks(int minX, int minZ, int genSize, Steps targetStep, double runTimeRatio, Consumer resultConsumer); + } diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/TestWorldGenerator.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/TestWorldGenerator.java new file mode 100644 index 000000000..7645bb288 --- /dev/null +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/TestWorldGenerator.java @@ -0,0 +1,70 @@ +package testItems.worldGeneratorInjection.objects; + +import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenThreadMode; +import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; +import com.seibel.lod.core.DependencyInjection.OverrideInjector; +import com.seibel.lod.core.util.LodUtil; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +/** + * Dummy test implementation object for world generator injection unit tests. + * + * @author James Seibel + * @version 2022-12-5 + */ +public class TestWorldGenerator implements IDhApiWorldGenerator +{ + public static EDhApiWorldGenThreadMode THREAD_MODE = EDhApiWorldGenThreadMode.SINGLE_THREADED; + + + // testable methods // + + @Override + public int getPriority() { return OverrideInjector.CORE_PRIORITY; } + + @Override + public EDhApiWorldGenThreadMode getThreadingMode() { return THREAD_MODE; } + + + + + // not used when unit testing // + + //======================// + // generator parameters // + //======================// + + @Override + public byte getMinDataDetailLevel() { return LodUtil.BLOCK_DETAIL_LEVEL; } + + @Override + public byte getMaxDataDetailLevel() { return LodUtil.BLOCK_DETAIL_LEVEL; } + + @Override + public byte getMinGenerationGranularity() { return LodUtil.CHUNK_DETAIL_LEVEL; } + + @Override + public byte getMaxGenerationGranularity() { return LodUtil.CHUNK_DETAIL_LEVEL + 2; } + + + //===================// + // generator methods // + //===================// + + @Override + public void close() { } + + @Override + public boolean isBusy() { return false; } + + @Override + public CompletableFuture generateChunks(int chunkPosMinX, int chunkPosMinZ, byte granularity, byte targetDataDetail, Consumer resultConsumer) { return null; } + + @Override + public void preGeneratorTaskStart() { } + + +} + diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestCore.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestCore.java index 97b15b08a..368e8d2a7 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestCore.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestCore.java @@ -1,49 +1,23 @@ package testItems.worldGeneratorInjection.objects; import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenThreadMode; -import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenerationStep; -import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; -import com.seibel.lod.api.interfaces.world.IDhApiChunkWrapper; -import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.lod.core.DependencyInjection.OverrideInjector; - /** * Dummy test implementation object for world generator injection unit tests. * * @author James Seibel - * @version 2022-7-26 + * @version 2022-12-5 */ -public class WorldGeneratorTestCore implements IDhApiWorldGenerator +public class WorldGeneratorTestCore extends TestWorldGenerator { + public static int PRIORITY = OverrideInjector.CORE_PRIORITY; public static EDhApiWorldGenThreadMode THREAD_MODE = EDhApiWorldGenThreadMode.SINGLE_THREADED; - //==============// - // IOverridable // - //==============// - @Override - public int getPriority() { return OverrideInjector.CORE_PRIORITY; } - - - - //======================// - // IDhApiWorldGenerator // - //======================// - - - /** Returns which thread chunk generation requests can be created on. */ - @Override - public EDhApiWorldGenThreadMode getThreadingMode() - { - return THREAD_MODE; - } + public int getPriority() { return PRIORITY; } @Override - public IDhApiChunkWrapper generateCoreChunk(int chunkPosX, int chunkPosZ, IDhApiLevelWrapper serverLevelWrapper, EDhApiWorldGenerationStep maxStepToGenerate) - { - return null; - } - + public EDhApiWorldGenThreadMode getThreadingMode() { return THREAD_MODE; } } diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestPrimary.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestPrimary.java index 7cfee8c26..3091c7a9a 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestPrimary.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestPrimary.java @@ -11,34 +11,19 @@ import com.seibel.lod.core.DependencyInjection.OverrideInjector; * Dummy test implementation object for world generator injection unit tests. * * @author James Seibel - * @version 2022-8-9 + * @version 2022-12-5 */ -public class WorldGeneratorTestPrimary implements IDhApiWorldGenerator +public class WorldGeneratorTestPrimary extends TestWorldGenerator { public static int PRIORITY = OverrideInjector.DEFAULT_NON_CORE_OVERRIDE_PRIORITY + 5; public static EDhApiWorldGenThreadMode THREAD_MODE = EDhApiWorldGenThreadMode.MULTI_THREADED; - //==============// - // IOverridable // - //==============// - + @Override public int getPriority() { return PRIORITY; } - - - - //======================// - // IDhApiWorldGenerator // - //======================// - + @Override public EDhApiWorldGenThreadMode getThreadingMode() { return THREAD_MODE; } - @Override - public IDhApiChunkWrapper generateCoreChunk(int chunkPosX, int chunkPosZ, IDhApiLevelWrapper serverLevelWrapper, EDhApiWorldGenerationStep maxStepToGenerate) - { - return null; - } - } diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestSecondary.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestSecondary.java index 530cae0f4..212d6748e 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestSecondary.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/WorldGeneratorTestSecondary.java @@ -11,34 +11,18 @@ import com.seibel.lod.core.DependencyInjection.OverrideInjector; * Dummy test implementation object for world generator injection unit tests. * * @author James Seibel - * @version 2022-8-9 + * @version 2022-12-5 */ -public class WorldGeneratorTestSecondary implements IDhApiWorldGenerator +public class WorldGeneratorTestSecondary extends TestWorldGenerator { public static int PRIORITY = OverrideInjector.DEFAULT_NON_CORE_OVERRIDE_PRIORITY; public static EDhApiWorldGenThreadMode THREAD_MODE = EDhApiWorldGenThreadMode.SERVER_THREAD; - - - //==============// - // IOverridable // - //==============// - + + @Override public int getPriority() { return PRIORITY; } - - - - //======================// - // IDhApiWorldGenerator // - //======================// @Override public EDhApiWorldGenThreadMode getThreadingMode() { return THREAD_MODE; } - @Override - public IDhApiChunkWrapper generateCoreChunk(int chunkPosX, int chunkPosZ, IDhApiLevelWrapper serverLevelWrapper, EDhApiWorldGenerationStep maxStepToGenerate) - { - return null; - } - }