Generate block stub classes
This commit is contained in:
parent
50710d6240
commit
edc93f7a80
11 changed files with 178 additions and 73 deletions
|
@ -40,6 +40,10 @@ dependencies {
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets["main"].kotlin.srcDir("src/main/generatedKotlin")
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package space.blokk.plugin
|
package space.blokk.plugin
|
||||||
|
|
||||||
interface Plugin {
|
interface Plugin {
|
||||||
|
fun onEnable() {}
|
||||||
|
fun onDisable() {}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getCalling(): Plugin? {
|
fun getCalling(): Plugin? {
|
||||||
// TODO: Return the last plugin in the stacktrace
|
// TODO: Return the last plugin in the stacktrace
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package space.blokk.world
|
package space.blokk.world
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import space.blokk.world.block.Block
|
import space.blokk.world.block.BlockRef
|
||||||
|
|
||||||
interface Chunk {
|
interface Chunk {
|
||||||
/**
|
/**
|
||||||
|
@ -9,6 +9,8 @@ interface Chunk {
|
||||||
*/
|
*/
|
||||||
val key: Key
|
val key: Key
|
||||||
|
|
||||||
|
data class Key(val x: Int, val z: Int)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [CoroutineScope] of this chunk.
|
* [CoroutineScope] of this chunk.
|
||||||
*
|
*
|
||||||
|
@ -16,24 +18,35 @@ interface Chunk {
|
||||||
*/
|
*/
|
||||||
val scope: CoroutineScope
|
val scope: CoroutineScope
|
||||||
|
|
||||||
data class Key(val x: Int, val z: Int)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [world][World] this chunk belongs to.
|
* The [World] this chunk belongs to.
|
||||||
*/
|
*/
|
||||||
val world: World
|
val world: World
|
||||||
|
|
||||||
|
// TODO: Allow more than 16 sections
|
||||||
|
/**
|
||||||
|
* The 16 [ChunkSection]s this chunk consists of.
|
||||||
|
*/
|
||||||
val sections: Array<ChunkSection>
|
val sections: Array<ChunkSection>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a block in this chunk.
|
* Whether this chunk must be loaded using [load] before it can be accessed.
|
||||||
|
*
|
||||||
|
* This value should not change for the lifetime of a chunk.
|
||||||
|
*/
|
||||||
|
val mustBeLoaded: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [BlockRef] for the specified coordinates in this chunk.
|
||||||
*
|
*
|
||||||
* **The coordinates are relative to this chunk, not to the entire world.**
|
* **The coordinates are relative to this chunk, not to the entire world.**
|
||||||
*/
|
*/
|
||||||
fun getBlock(x: Byte, y: Int, z: Byte): Block
|
fun getBlock(x: Byte, y: Int, z: Byte): BlockRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads this chunk into memory.
|
* Loads this chunk into memory.
|
||||||
|
*
|
||||||
|
* If the implementation does not need to load chunks, this method should do nothing.
|
||||||
*/
|
*/
|
||||||
suspend fun load()
|
suspend fun load()
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,13 @@ package space.blokk.world
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import space.blokk.CoordinatePartOrder
|
import space.blokk.CoordinatePartOrder
|
||||||
import space.blokk.entity.Entity
|
import space.blokk.entity.Entity
|
||||||
import space.blokk.world.block.Block
|
import space.blokk.world.block.BlockRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Minecraft world, sometimes also called level.
|
* A Minecraft world, sometimes also called level.
|
||||||
|
*
|
||||||
|
* **Note: Although the methods in this class are called `getBlock`, `getBlocksInSphere` and so on, they actually
|
||||||
|
* return [BlockRef]s.**
|
||||||
*/
|
*/
|
||||||
abstract class World {
|
abstract class World {
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +41,8 @@ abstract class World {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the chunk key for the chunk containing the block at [location].
|
* Gets the chunk key for the chunk containing the block at [location].
|
||||||
|
*
|
||||||
|
* @public
|
||||||
*/
|
*/
|
||||||
fun getChunkKeyAt(location: BlockLocation) =
|
fun getChunkKeyAt(location: BlockLocation) =
|
||||||
Chunk.Key(location.x / Chunk.WIDTH_AND_LENGTH, location.y / Chunk.WIDTH_AND_LENGTH)
|
Chunk.Key(location.x / Chunk.WIDTH_AND_LENGTH, location.y / Chunk.WIDTH_AND_LENGTH)
|
||||||
|
@ -55,7 +60,7 @@ abstract class World {
|
||||||
/**
|
/**
|
||||||
* @return The block at [location].
|
* @return The block at [location].
|
||||||
*/
|
*/
|
||||||
abstract fun getBlock(location: BlockLocation): Block
|
abstract fun getBlock(location: BlockLocation): BlockRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param order The order of the arrays.
|
* @param order The order of the arrays.
|
||||||
|
@ -66,7 +71,7 @@ abstract class World {
|
||||||
firstCorner: BlockLocation,
|
firstCorner: BlockLocation,
|
||||||
secondCorner: BlockLocation,
|
secondCorner: BlockLocation,
|
||||||
order: CoordinatePartOrder = CoordinatePartOrder.DEFAULT
|
order: CoordinatePartOrder = CoordinatePartOrder.DEFAULT
|
||||||
): Array<Array<Array<Block>>> {
|
): Array<Array<Array<BlockRef>>> {
|
||||||
val start = firstCorner.withLowestValues(secondCorner)
|
val start = firstCorner.withLowestValues(secondCorner)
|
||||||
val end = firstCorner.withHighestValues(secondCorner)
|
val end = firstCorner.withHighestValues(secondCorner)
|
||||||
|
|
||||||
|
@ -85,7 +90,8 @@ abstract class World {
|
||||||
fun getBlocksInSphere(
|
fun getBlocksInSphere(
|
||||||
center: BlockLocation,
|
center: BlockLocation,
|
||||||
radius: Int
|
radius: Int
|
||||||
): Array<Block> {
|
): Array<BlockRef> {
|
||||||
|
// https://www.reddit.com/r/VoxelGameDev/comments/2cttnt/how_to_create_a_sphere_out_of_voxels/
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
package space.blokk.world.block
|
package space.blokk.world.block
|
||||||
|
|
||||||
import space.blokk.world.BlockLocation
|
|
||||||
import space.blokk.world.Chunk
|
|
||||||
import space.blokk.world.World
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a block in a chunk of a world.
|
* @param ref The [BlockRef] referencing this block.
|
||||||
*
|
|
||||||
* There is always only one instance for every [location][BlockLocation] in a world.
|
|
||||||
*/
|
*/
|
||||||
interface Block {
|
abstract class Block internal constructor(val ref: BlockRef) {
|
||||||
/**
|
/**
|
||||||
* The [location][BlockLocation] of this block.
|
* The [Material] of this block.
|
||||||
*/
|
*/
|
||||||
val location: BlockLocation
|
abstract val material: Material
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [World] containing this block.
|
* Whether [ref] still references this block.
|
||||||
*/
|
*/
|
||||||
val world: World
|
val destroyed: Boolean get() = ref.block === this
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [Chunk] containing this block.
|
* Replaces this block with air.
|
||||||
*/
|
*/
|
||||||
val chunk: Chunk get() = world.getChunkAt(location)
|
fun replaceWithAir() {
|
||||||
|
checkNotDestroyed()
|
||||||
|
ref.place(Material.AIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkNotDestroyed() {
|
||||||
|
if (destroyed) throw IllegalStateException("The block was destroyed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when [ref] no longer references this block.
|
||||||
|
*/
|
||||||
|
internal abstract fun destroy()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
package space.blokk.world.block
|
|
||||||
|
|
||||||
interface BlockContent
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package space.blokk.world.block
|
||||||
|
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import space.blokk.world.BlockLocation
|
||||||
|
import space.blokk.world.Chunk
|
||||||
|
import space.blokk.world.World
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to a block in a chunk of a world.
|
||||||
|
*
|
||||||
|
* There is always only one instance for every [location][BlockLocation] in a world.
|
||||||
|
*/
|
||||||
|
abstract class BlockRef {
|
||||||
|
/**
|
||||||
|
* The [location][BlockLocation] of this ref.
|
||||||
|
*/
|
||||||
|
abstract val location: BlockLocation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [World] containing this ref.
|
||||||
|
*/
|
||||||
|
abstract val world: World
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Chunk] containing this ref.
|
||||||
|
*/
|
||||||
|
val chunk: Chunk get() = world.getChunkAt(location)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current [Block] of this ref.
|
||||||
|
*/
|
||||||
|
abstract var block: Block; protected set
|
||||||
|
|
||||||
|
private val placeMutex by lazy { Mutex() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [Block] and assign it to this ref (i.e. place it in the world).
|
||||||
|
*/
|
||||||
|
fun place(material: Material): Block = TODO()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [Block] and assign it to this ref (i.e. place it in the world).
|
||||||
|
*/
|
||||||
|
suspend fun <T : Block> place(type: KClass<T>): T = type.primaryConstructor!!.call(this).also {
|
||||||
|
placeMutex.withLock {
|
||||||
|
block.destroy()
|
||||||
|
block = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun of(world: World, location: BlockLocation) = world.getBlock(location)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
package space.blokk.world.block
|
|
||||||
|
|
||||||
import space.blokk.util.KClassToInstanceMap
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
abstract class BlockType internal constructor(
|
|
||||||
val id: Int,
|
|
||||||
val name: String,
|
|
||||||
val hardness: Float,
|
|
||||||
val contentType: KClass<out BlockContent>,
|
|
||||||
val transparent: Boolean,
|
|
||||||
val emittedLight: Byte,
|
|
||||||
val filteredLight: Byte,
|
|
||||||
val collisionShape: Array<Array<Double>>,
|
|
||||||
val maxStackSize: Byte
|
|
||||||
) {
|
|
||||||
private val extensions = KClassToInstanceMap<Extension>()
|
|
||||||
|
|
||||||
operator fun <T : Extension> get(key: KClassToInstanceMap.Key<T>): T = get(key.actualKey)
|
|
||||||
operator fun <T : Extension> get(key: KClass<out T>): T =
|
|
||||||
extensions.getInstance(key) ?: throw ExtensionNotProvidedException()
|
|
||||||
|
|
||||||
fun <T : Extension> provideExtension(key: KClassToInstanceMap.Key<T>, value: T) = extensions.putInstance(key, value)
|
|
||||||
|
|
||||||
class ExtensionNotProvidedException : Exception("The specified extension is not provided for this BlockType")
|
|
||||||
|
|
||||||
abstract class Extension
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
package space.blokk.world.block
|
|
||||||
|
|
||||||
interface Stone : BlockContent {
|
|
||||||
}
|
|
|
@ -2,17 +2,20 @@ package space.blokk.mdsp
|
||||||
|
|
||||||
import com.google.common.base.CaseFormat
|
import com.google.common.base.CaseFormat
|
||||||
import com.jsoniter.JsonIterator
|
import com.jsoniter.JsonIterator
|
||||||
import com.jsoniter.any.Any
|
|
||||||
import com.squareup.kotlinpoet.*
|
import com.squareup.kotlinpoet.*
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
typealias JsonAny = com.jsoniter.any.Any
|
typealias JsonAny = com.jsoniter.any.Any
|
||||||
|
|
||||||
class FilesGenerator(private val dataDir: File, private val outputDir: File) {
|
class FilesGenerator(private val dataDir: File, private val outputDir: File, private val sourcesDir: File) {
|
||||||
companion object {
|
companion object {
|
||||||
const val API_PACKAGE = "space.blokk"
|
const val API_PACKAGE = "space.blokk"
|
||||||
const val BLOCK_PACKAGE = "space.blokk.world.block"
|
const val BLOCK_PACKAGE = "space.blokk.world.block"
|
||||||
|
val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material")
|
||||||
|
val BLOCK_TYPE = ClassName(BLOCK_PACKAGE, "Block")
|
||||||
|
val BLOCK_REF_TYPE = ClassName(BLOCK_PACKAGE, "BlockRef")
|
||||||
|
val K_CLASS_TYPE = ClassName("kotlin.reflect", "KClass")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generate() {
|
fun generate() {
|
||||||
|
@ -24,10 +27,12 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File) {
|
||||||
|
|
||||||
val dataJson = dataDir.resolve("blocks.json").readText()
|
val dataJson = dataDir.resolve("blocks.json").readText()
|
||||||
val blocks = JsonIterator.deserialize(dataJson).asList()
|
val blocks = JsonIterator.deserialize(dataJson).asList()
|
||||||
|
|
||||||
|
generateBlockStubs(blocks)
|
||||||
generateMaterialEnum(blocks, collisionShapesData.get("blocks").asMap())
|
generateMaterialEnum(blocks, collisionShapesData.get("blocks").asMap())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCollisionShapeEnum(shapes: Map<String, Any>) {
|
private fun generateCollisionShapeEnum(shapes: Map<String, JsonAny>) {
|
||||||
val builder = TypeSpec.enumBuilder("CollisionShape")
|
val builder = TypeSpec.enumBuilder("CollisionShape")
|
||||||
.primaryConstructor(
|
.primaryConstructor(
|
||||||
FunSpec.constructorBuilder()
|
FunSpec.constructorBuilder()
|
||||||
|
@ -38,6 +43,10 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File) {
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
.addKdoc(
|
||||||
|
"Explanation what the numbers mean: " +
|
||||||
|
"https://github.com/PrismarineJS/minecraft-data/blob/master/doc/blockCollisionShapes.md"
|
||||||
|
)
|
||||||
|
|
||||||
for (shape in shapes.toList().sortedBy { it.first.toInt() }) {
|
for (shape in shapes.toList().sortedBy { it.first.toInt() }) {
|
||||||
builder.addEnumConstant(shape.first, TypeSpec.anonymousClassBuilder()
|
builder.addEnumConstant(shape.first, TypeSpec.anonymousClassBuilder()
|
||||||
|
@ -55,21 +64,52 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File) {
|
||||||
.writeTo(outputDir)
|
.writeTo(outputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateMaterialEnum(blocks: List<JsonAny>, collisionShapeKeyByBlockName: MutableMap<String, Any>) {
|
private fun generateBlockStubs(blocks: List<JsonAny>) {
|
||||||
|
for (block in blocks) {
|
||||||
|
val lowerUnderscoreName = block.get("name").toString()
|
||||||
|
val upperCamelName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, lowerUnderscoreName)
|
||||||
|
val upperUnderscoreName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, lowerUnderscoreName)
|
||||||
|
val specificMaterialType = MATERIAL_TYPE.nestedClass(upperUnderscoreName)
|
||||||
|
|
||||||
|
if (sourcesDir.resolve("./${BLOCK_PACKAGE.replace(".", "/")}/$upperCamelName.kt").exists())
|
||||||
|
continue
|
||||||
|
|
||||||
|
val type = TypeSpec.classBuilder(upperCamelName)
|
||||||
|
.addKdoc("A [$upperUnderscoreName][%T] block", specificMaterialType)
|
||||||
|
.addModifiers(KModifier.ABSTRACT)
|
||||||
|
.superclass(BLOCK_TYPE)
|
||||||
|
.primaryConstructor(
|
||||||
|
FunSpec.constructorBuilder()
|
||||||
|
.addParameter("ref", BLOCK_REF_TYPE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.addSuperclassConstructorParameter("ref")
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("material", MATERIAL_TYPE, KModifier.OVERRIDE)
|
||||||
|
.initializer("%T", specificMaterialType)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
FileSpec.builder(BLOCK_PACKAGE, upperCamelName)
|
||||||
|
.addComment("IF YOU CHANGE THIS FILE, MOVE IT FROM `generatedKotlin` TO `kotlin` OR IT WILL BE OVERWRITTEN.")
|
||||||
|
.addType(type)
|
||||||
|
.build()
|
||||||
|
.writeTo(outputDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMaterialEnum(blocks: List<JsonAny>, collisionShapeKeyByBlockName: MutableMap<String, JsonAny>) {
|
||||||
val builder = TypeSpec.enumBuilder("Material")
|
val builder = TypeSpec.enumBuilder("Material")
|
||||||
.primaryConstructor(
|
.primaryConstructor(
|
||||||
FunSpec.constructorBuilder()
|
FunSpec.constructorBuilder()
|
||||||
.addParameter("id", Int::class)
|
.addParameter("id", Int::class)
|
||||||
.addParameter("name", String::class)
|
.addParameter("name", String::class)
|
||||||
|
.addParameter(
|
||||||
|
"blockType",
|
||||||
|
K_CLASS_TYPE.parameterizedBy(WildcardTypeName.producerOf(BLOCK_TYPE))
|
||||||
|
)
|
||||||
.addParameter("hardness", Float::class)
|
.addParameter("hardness", Float::class)
|
||||||
// TODO: Uncomment when all block content interfaces exist
|
|
||||||
// .addParameter(
|
|
||||||
// "contentType",
|
|
||||||
// ClassName(
|
|
||||||
// "kotlin.reflect",
|
|
||||||
// "KClass"
|
|
||||||
// ).parameterizedBy(ClassName(BLOCK_PACKAGE, "BlockContent"))
|
|
||||||
// )
|
|
||||||
.addParameter("transparent", Boolean::class)
|
.addParameter("transparent", Boolean::class)
|
||||||
.addParameter("emittedLight", Byte::class)
|
.addParameter("emittedLight", Byte::class)
|
||||||
.addParameter("filteredLight", Byte::class)
|
.addParameter("filteredLight", Byte::class)
|
||||||
|
@ -83,10 +123,17 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File) {
|
||||||
val collisionShapeValue = collisionShapeKeyByBlockName[name]!!
|
val collisionShapeValue = collisionShapeKeyByBlockName[name]!!
|
||||||
|
|
||||||
builder.addEnumConstant(
|
builder.addEnumConstant(
|
||||||
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, block.get("name").toString()),
|
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, name),
|
||||||
TypeSpec.anonymousClassBuilder()
|
TypeSpec.anonymousClassBuilder()
|
||||||
.addSuperclassConstructorParameter("%L", block.get("id").toInt())
|
.addSuperclassConstructorParameter("%L", block.get("id").toInt())
|
||||||
.addSuperclassConstructorParameter("%S", name)
|
.addSuperclassConstructorParameter("%S", name)
|
||||||
|
.addSuperclassConstructorParameter(
|
||||||
|
"%T::class",
|
||||||
|
ClassName(
|
||||||
|
BLOCK_PACKAGE,
|
||||||
|
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name)
|
||||||
|
)
|
||||||
|
)
|
||||||
.addSuperclassConstructorParameter("%Lf", block.get("hardness").toFloat())
|
.addSuperclassConstructorParameter("%Lf", block.get("hardness").toFloat())
|
||||||
.addSuperclassConstructorParameter("%L", block.get("transparent").toBoolean())
|
.addSuperclassConstructorParameter("%L", block.get("transparent").toBoolean())
|
||||||
.addSuperclassConstructorParameter("%L", block.get("emitLight").toInt().toByte())
|
.addSuperclassConstructorParameter("%L", block.get("emitLight").toInt().toByte())
|
||||||
|
|
|
@ -23,7 +23,11 @@ class MinecraftDataSourcesPlugin : Plugin<Project> {
|
||||||
project
|
project
|
||||||
.project(":blokk-api")
|
.project(":blokk-api")
|
||||||
.projectDir
|
.projectDir
|
||||||
.resolve("src/main/generatedKotlin")
|
.resolve("src/main/generatedKotlin"),
|
||||||
|
project
|
||||||
|
.project(":blokk-api")
|
||||||
|
.projectDir
|
||||||
|
.resolve("src/main/kotlin")
|
||||||
).generate()
|
).generate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue