Update the API to allow for N-sized world generation requests

This breaks old world generators
This commit is contained in:
James Seibel
2024-10-07 19:45:28 -05:00
parent 28ec1e2960
commit 1b59a269e6
11 changed files with 325 additions and 227 deletions
@@ -19,8 +19,8 @@
package com.seibel.distanthorizons.api.enums.worldGeneration;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
@@ -28,16 +28,17 @@ import java.util.function.Consumer;
/**
* VANILLA_CHUNKS, <br>
* API_CHUNKS <br>
* API_DATA_SOURCES <br>
*
* @author Builderb0y, James Seibel
* @version 2023-12-21
* @version 2024-10-5
* @since API 2.0.0
*/
public enum EDhApiWorldGeneratorReturnType
{
/**
* when this constant is returned by {@link IDhApiWorldGenerator#getReturnType()},
* {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* {@link IDhApiWorldGenerator#generateChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* will be used when generating terrain.
*
* @since API 2.0.0
@@ -46,11 +47,20 @@ public enum EDhApiWorldGeneratorReturnType
/**
* when this constant is returned by {@link IDhApiWorldGenerator#getReturnType()},
* {@link IDhApiWorldGenerator#generateApiChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* {@link IDhApiWorldGenerator#generateApiChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* will be used when generating terrain.
*
* @since API 2.0.0
*/
API_CHUNKS;
API_CHUNKS,
/**
* when this constant is returned by {@link IDhApiWorldGenerator#getReturnType()},
* {@link IDhApiWorldGenerator#generateLod(int, int, byte, IDhApiFullDataSource, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* will be used when generating terrain.
*
* @since API 4.0.0
*/
API_DATA_SOURCES;
}
@@ -46,10 +46,6 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
public final byte getSmallestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; }
@Override
public final byte getLargestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; }
@Override
public final byte getMinGenerationGranularity() { return EDhApiDetailLevel.CHUNK.detailLevel; }
@Override
public final byte getMaxGenerationGranularity() { return (byte) (EDhApiDetailLevel.CHUNK.detailLevel + 2); }
@@ -60,17 +56,14 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
@Override
public final CompletableFuture<Void> generateChunks(
int chunkPosMinX, int chunkPosMinZ,
byte granularity, byte targetDataDetail, EDhApiDistantGeneratorMode generatorMode,
int generationRequestChunkWidthCount, byte targetDataDetail, EDhApiDistantGeneratorMode generatorMode,
ExecutorService worldGeneratorThreadPool, Consumer<Object[]> resultConsumer) throws ClassCastException
{
return CompletableFuture.runAsync(() ->
{
// TODO what does this mean?
int genChunkWidth = BitShiftUtil.powerOfTwo(granularity - 4);
for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + genChunkWidth; chunkX++)
for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + generationRequestChunkWidthCount; chunkX++)
{
for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + genChunkWidth; chunkZ++)
for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + generationRequestChunkWidthCount; chunkZ++)
{
Object[] rawMcObjectArray = this.generateChunk(chunkX, chunkZ, generatorMode);
resultConsumer.accept(rawMcObjectArray);
@@ -83,7 +76,7 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
public final CompletableFuture<Void> generateApiChunks(
int chunkPosMinX,
int chunkPosMinZ,
byte granularity,
int generationRequestChunkWidthCount,
byte targetDataDetail,
EDhApiDistantGeneratorMode generatorMode,
ExecutorService worldGeneratorThreadPool,
@@ -92,12 +85,9 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
{
return CompletableFuture.runAsync(() ->
{
// TODO what does this mean?
int genChunkWidth = BitShiftUtil.powerOfTwo(granularity - 4);
for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + genChunkWidth; chunkX++)
for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + generationRequestChunkWidthCount; chunkX++)
{
for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + genChunkWidth; chunkZ++)
for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + generationRequestChunkWidthCount; chunkZ++)
{
DhApiChunk apiChunk = this.generateApiChunk(chunkX, chunkZ, generatorMode);
resultConsumer.accept(apiChunk);
@@ -115,10 +105,10 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
* @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level)
* @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
*
* @return See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks}
* @return See {@link IDhApiWorldGenerator#generateChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks}
* for the list of Object's this method should return along with additional documentation.
*
* @see IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator#generateChunks
* @see IDhApiWorldGenerator#generateChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator#generateChunks
*/
public abstract Object[] generateChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode);
@@ -133,7 +123,7 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
* @return A {@link DhApiChunk} with the generated {@link DhApiTerrainDataPoint} including air blocks.
* Note: if air blocks aren't included with the proper lighting, lower detail levels will appear as black/unlit.
*
* @see IDhApiWorldGenerator#generateApiChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)
* @see IDhApiWorldGenerator#generateApiChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)
*
* @since API 3.0.0
*/
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import java.io.Closeable;
import java.util.concurrent.CompletableFuture;
@@ -32,7 +33,7 @@ import java.util.function.Consumer;
/**
* @author James Seibel
* @version 2023-6-22
* @version 2024-10-07
* @since API 1.0.0
*/
public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
@@ -43,19 +44,17 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
/**
* Defines the smallest datapoint size that can be generated at a time. <br>
* Minimum detail level is 0 (1 block) <br>
* Maximum detail level (smallest numerical value) is 0 (1 block) <br>
* Default detail level is 0 <br>
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}. <br><br>
*
* TODO: System currently only supports 1x1 block per data.
*
*
* @see EDhApiDetailLevel
* @since API 1.0.0
*/
default byte getSmallestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; }
/**
* Defines the largest datapoint size that can be generated at a time. <br>
* Minimum detail level is 0 (1 block) <br>
* Maximum detail level (smallest numerical value) is 0 (1 block) <br>
* Default detail level is 0 <br>
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}.
*
@@ -64,56 +63,18 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*/
default byte getLargestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; }
/**
* When creating generation requests the system will attempt to group nearby tasks together. <br><br>
* What is the minimum size a single generation call can batch together? <br>
*
* Minimum detail level is 4 (the size of a MC chunk) <br>
* Default detail level is 4 <br>
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}.
*
* @see EDhApiDetailLevel
* @since API 1.0.0
*/
default byte getMinGenerationGranularity() { return EDhApiDetailLevel.CHUNK.detailLevel; }
/**
* When creating generation requests the system will attempt to group nearby tasks together. <br><br>
* What is the maximum size a single generation call can batch together? <br>
*
* Minimum detail level is 4 (the size of a MC chunk) <br>
* Default detail level is 6 (4x4 chunks) <br>
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}.
*
* @see EDhApiDetailLevel
* @since API 1.0.0
*/
default byte getMaxGenerationGranularity() { return (byte) (EDhApiDetailLevel.CHUNK.detailLevel + 2); }
/**
* Starting in API 3.0.0 DH now handles future queuing/management internally. <br><br>
*
* Previous description: <br>
* true if the generator is unable to accept new generation requests. <br>
*
* @since API 1.0.0
* @deprecated API 3.0.0
*/
@Deprecated
default boolean isBusy() { return false; }
/**
* Only used if {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}. <Br>
* If true DH will run additional validation on the {@link DhApiChunk}'s returned. <Br>
* Used if {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS} or {@link EDhApiWorldGeneratorReturnType#API_DATA_SOURCES}. <Br>
* If true DH will run additional validation on the {@link DhApiChunk} or {@link IDhApiFullDataSource}'s returned. <Br>
* This should be disabled during release but should be enabled during development to help spot issues with your data format.
*
* @see #getReturnType()
* @see DhApiChunk
* @see IDhApiFullDataSource
* @see EDhApiWorldGeneratorReturnType#API_CHUNKS
* @since API 3.0.0
* @since API 4.0.0
*/
default boolean runApiChunkValidation() { return true; }
default boolean runApiValidation() { return true; }
@@ -143,9 +104,9 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*
* @param chunkPosMinX the chunk X position closest to negative infinity
* @param chunkPosMinZ the chunk Z position closest to negative infinity
* @param granularity TODO find a central location to store the definition of granularity. For now it is stored in the Core method: WorldGenerationQueue#startGenerationEvent
* @param generationRequestChunkWidthCount how many chunks wide you should generate
* @param targetDataDetail the LOD Detail level requested to generate. See {@link EDhApiDetailLevel} for additional information.
* @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
* @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
* @param worldGeneratorThreadPool the thread pool that should be used when generating the returned {@link CompletableFuture}.
* @param resultConsumer the consumer that should be fired whenever a chunk finishes generating.
*
@@ -156,7 +117,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
default CompletableFuture<Void> generateChunks(
int chunkPosMinX,
int chunkPosMinZ,
byte granularity,
int generationRequestChunkWidthCount,
byte targetDataDetail,
EDhApiDistantGeneratorMode generatorMode,
ExecutorService worldGeneratorThreadPool,
@@ -165,7 +126,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
{
throw new UnsupportedOperationException();
}
/**
* This method is called by Distant Horizons to generate terrain over a given area when
* {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}. <br><br>
@@ -179,9 +140,9 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*
* @param chunkPosMinX the chunk X position closest to negative infinity
* @param chunkPosMinZ the chunk Z position closest to negative infinity
* @param granularity TODO find a central location to store the definition of granularity. For now it is stored in the Core method: WorldGenerationQueue#startGenerationEvent
* @param generationRequestChunkWidthCount how many chunks wide you should generate
* @param targetDataDetail the LOD Detail level requested to generate. See {@link EDhApiDetailLevel} for additional information.
* @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
* @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
* @param worldGeneratorThreadPool the thread pool that should be used when generating the returned {@link CompletableFuture}.
* @param resultConsumer the consumer that should be fired whenever a chunk finishes generating.
*
@@ -192,7 +153,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
default CompletableFuture<Void> generateApiChunks(
int chunkPosMinX,
int chunkPosMinZ,
byte granularity,
int generationRequestChunkWidthCount,
byte targetDataDetail,
EDhApiDistantGeneratorMode generatorMode,
ExecutorService worldGeneratorThreadPool,
@@ -201,11 +162,51 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
{
throw new UnsupportedOperationException();
}
/**
* This method is called by Distant Horizons to generate terrain over a given area when
* {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_DATA_SOURCES}. <br><br>
*
* After the {@link IDhApiWorldGenerator} has been generated, it should be passed into the
* resultConsumer's {@link Consumer#accept(Object)} method.
* Note: if air blocks aren't included in the with the {@link DhApiChunk} with proper lighting, lower detail levels will appear as black/unlit.
*
* @implNote the default implementation of this method throws an {@link UnsupportedOperationException},
* and must be overridden when {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}.
*
* @param chunkPosMinX the chunk X position closest to negative infinity
* @param chunkPosMinZ the chunk Z position closest to negative infinity
* @param lodPosX the LOD's X position, relative to the given {@link EDhApiDetailLevel}
* @param lodPosZ the LOD's Z position, relative to the given {@link EDhApiDetailLevel}
* @param detailLevel the LOD Detail level requested to generate. See {@link EDhApiDetailLevel} for additional information.
* @param pooledFullDataSource The data source you should populate during your world generation.
* This data source is pooled by DH and may be reused multiple times by different internal DH systems. <br>
* This data source should <strong>not</strong> be referenced or stored outside of this method nor the executor provided by worldGeneratorThreadPool.
* <strong>Attempting to do so will corrupt DH's data.</strong>
* @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
* @param worldGeneratorThreadPool the thread pool that should be used when generating the returned {@link CompletableFuture}.
* @param resultConsumer the consumer that should be fired whenever a chunk finishes generating.
*
* @return a future that should run on the worldGeneratorThreadPool and complete once the given generation task has completed.
*
* @since API 4.0.0
*/
default CompletableFuture<Void> generateLod(
int chunkPosMinX, int chunkPosMinZ,
int lodPosX, int lodPosZ, byte detailLevel,
IDhApiFullDataSource pooledFullDataSource,
EDhApiDistantGeneratorMode generatorMode,
ExecutorService worldGeneratorThreadPool,
Consumer<IDhApiFullDataSource> resultConsumer
)
{
throw new UnsupportedOperationException();
}
/**
* This method controls how Distant Horizons requests generated chunks.
* By default, the return value is {@link EDhApiWorldGeneratorReturnType#VANILLA_CHUNKS},
* which means that {@link #generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* which means that {@link #generateChunks(int, int, int, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)}
* will be invoked whenever Distant Horizons wants to generate terrain with this world generator.
*
* @since API 2.0.0
@@ -238,4 +239,5 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
void close();
}
@@ -0,0 +1,45 @@
package com.seibel.distanthorizons.api.objects.data;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import java.util.List;
/**
* Represents a single full LOD backed by Distant Horizons' ID system.
*
* @see IDhApiWorldGenerator
* @since API 4.0.0
*/
public interface IDhApiFullDataSource
{
/** @return how many data columns wide this data source is */
int getWidthInDataColumns();
/**
* Sets the data column at the relative X and Z position to the list given.
* The given list may be resorted based on the internal format DH requires.
*
* @param relX can be in the range 0 to {@link IDhApiFullDataSource#getWidthInDataColumns()}-1 (both inclusive)
* @param relZ can be in the range 0 to {@link IDhApiFullDataSource#getWidthInDataColumns()}-1 (both inclusive)
*
* @return the same columnDataPoints list after it has been imported into the data source.
* The returned list and contained objects can then be re-used.
*
* @throws IndexOutOfBoundsException if the relative positions are negative or outside the bounds of this data source.
*/
List<DhApiTerrainDataPoint> setApiDataPointColumn(int relX, int relZ, List<DhApiTerrainDataPoint> columnDataPoints)
throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* @param relX can be in the range 0 to {@link IDhApiFullDataSource#getWidthInDataColumns()}-1 (both inclusive)
* @param relZ can be in the range 0 to {@link IDhApiFullDataSource#getWidthInDataColumns()}-1 (both inclusive)
*
* @return a {@link List} of {@link DhApiTerrainDataPoint} representing the data for the given relative position.
*
* @throws IndexOutOfBoundsException if the relative positions are negative or outside the bounds of this data source.
*/
List<DhApiTerrainDataPoint> getApiDataPointColumn(int relX, int relZ) throws IndexOutOfBoundsException;
}