From 4cc3d8f17fa3de997306c994bb1b3d09e3432db6 Mon Sep 17 00:00:00 2001 From: Moritz Ruth Date: Sun, 15 Nov 2020 21:56:17 +0100 Subject: [PATCH] Add block codecs --- blokk-api/build.gradle.kts | 2 +- .../space/blokk/util/KPropertyValuePair.kt | 5 ++ .../space/blokk/world/block/AcaciaButton.kt | 13 ----- .../kotlin/space/blokk/world/block/Block.kt | 53 +++++++++++++++++++ .../world/block/{interfaces => }/Button.kt | 2 +- .../kotlin/space/blokk/world/block/Hopper.kt | 28 ++++++++++ blokk-block-codecs/build.gradle.kts | 25 +++++++++ .../world/block/codec/AcaciaButtonCodec.kt | 4 ++ .../main/kotlin/space/blokk/BlokkServer.kt | 5 ++ .../kotlin/space/blokk/mdsp/FilesGenerator.kt | 42 +++++++++++++-- 10 files changed, 159 insertions(+), 20 deletions(-) create mode 100644 blokk-api/src/main/kotlin/space/blokk/util/KPropertyValuePair.kt delete mode 100644 blokk-api/src/main/kotlin/space/blokk/world/block/AcaciaButton.kt rename blokk-api/src/main/kotlin/space/blokk/world/block/{interfaces => }/Button.kt (52%) create mode 100644 blokk-api/src/main/kotlin/space/blokk/world/block/Hopper.kt create mode 100644 blokk-block-codecs/build.gradle.kts create mode 100644 blokk-block-codecs/src/main/kotlin/space/blokk/world/block/codec/AcaciaButtonCodec.kt diff --git a/blokk-api/build.gradle.kts b/blokk-api/build.gradle.kts index 4980508..87f7379 100644 --- a/blokk-api/build.gradle.kts +++ b/blokk-api/build.gradle.kts @@ -20,7 +20,7 @@ val striktVersion = properties["version.strikt"].toString() dependencies { // Kotlin - implementation("org.jetbrains.kotlin:kotlin-reflect:1.4.10") + api("org.jetbrains.kotlin:kotlin-reflect:1.4.10") api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}") // JSON diff --git a/blokk-api/src/main/kotlin/space/blokk/util/KPropertyValuePair.kt b/blokk-api/src/main/kotlin/space/blokk/util/KPropertyValuePair.kt new file mode 100644 index 0000000..24a6c61 --- /dev/null +++ b/blokk-api/src/main/kotlin/space/blokk/util/KPropertyValuePair.kt @@ -0,0 +1,5 @@ +package space.blokk.util + +import kotlin.reflect.KProperty + +data class KPropertyValuePair(val property: KProperty, val value: V) diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/AcaciaButton.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/AcaciaButton.kt deleted file mode 100644 index e128312..0000000 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/AcaciaButton.kt +++ /dev/null @@ -1,13 +0,0 @@ -// IF YOU CHANGE THIS FILE, MOVE IT FROM `generatedKotlin` TO `kotlin` OR IT WILL BE OVERWRITTEN. -package space.blokk.world.block - -import space.blokk.world.block.interfaces.Button - -/** - * A block of type [ACACIA_BUTTON][Material.ACACIA_BUTTON] - */ -class AcaciaButton( - ref: BlockRef -) : Block(ref), Button { - override var pressed: Boolean = false -} diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/Block.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Block.kt index 8291704..8e6c83c 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/Block.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Block.kt @@ -1,5 +1,10 @@ package space.blokk.world.block +import space.blokk.util.KPropertyValuePair +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.full.starProjectedType + /** * @param ref The [BlockRef] referencing this block. */ @@ -26,4 +31,52 @@ abstract class Block internal constructor(val ref: BlockRef) { internal fun destroy() { onDestroy() } + + abstract class Companion { + abstract val codec: Codec<*> + } + + class Codec private constructor( + val blockClass: KClass, + val id: Int, + val firstStateID: Int, + val states: List>> + ) { + companion object { + inline fun id(id: Int, firstStateID: Int) = id(T::class, id, firstStateID) + fun id(blockClass: KClass, id: Int, firstStateID: Int) = + Builder(blockClass, id, firstStateID) + } + + class Builder internal constructor( + private val blockClass: KClass, + private val id: Int, + private val firstStateID: Int + ) { + private val attributeFields = mutableListOf>() + + fun attribute(property: KProperty) = this.apply { attributeFields.add(property) } + + fun build() = Codec(blockClass, id, firstStateID, generateStates(attributeFields)) + + companion object { + fun generateStates(properties: List>): List>> { + if (properties.isEmpty()) return emptyList() + + val current = properties[0] + println(current.returnType) + val allowedValues = when (current.returnType) { + Boolean::class.starProjectedType -> arrayOf(true, false) + else -> TODO() + } + + val rest = generateStates(properties.drop(1)) + return allowedValues.flatMap { value -> + if (rest.isEmpty()) listOf(arrayOf(KPropertyValuePair(current, value))) + else rest.map { arrayOf(KPropertyValuePair(current, value), *it) } + } + } + } + } + } } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/interfaces/Button.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Button.kt similarity index 52% rename from blokk-api/src/main/kotlin/space/blokk/world/block/interfaces/Button.kt rename to blokk-api/src/main/kotlin/space/blokk/world/block/Button.kt index d1693ba..27673b8 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/interfaces/Button.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Button.kt @@ -1,4 +1,4 @@ -package space.blokk.world.block.interfaces +package space.blokk.world.block interface Button { var pressed: Boolean diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/Hopper.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Hopper.kt new file mode 100644 index 0000000..4c8e7a0 --- /dev/null +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Hopper.kt @@ -0,0 +1,28 @@ +package space.blokk.world.block + +/** + * Material: [HOPPER][Material.HOPPER] + */ +class Hopper( + ref: BlockRef +) : Block(ref) { + var enabled: Boolean = false + + var facing: Facing = Facing.DOWN + + enum class Facing { + DOWN, + NORTH, + SOUTH, + WEST, + EAST + } + + companion object : Block.Companion() { + override val codec = Codec + .id(328, 6192) + .attribute(Hopper::enabled) + .attribute(Hopper::facing) + .build() + } +} diff --git a/blokk-block-codecs/build.gradle.kts b/blokk-block-codecs/build.gradle.kts new file mode 100644 index 0000000..fc104a6 --- /dev/null +++ b/blokk-block-codecs/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("jvm") +} + +group = rootProject.group +version = rootProject.version + +repositories { + mavenCentral() + maven("https://jitpack.io") +} + +dependencies { + +} + +tasks { + compileKotlin { + kotlinOptions.jvmTarget = "1.8" + kotlinOptions.freeCompilerArgs = listOf( + "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-progressive" + ) + } +} diff --git a/blokk-block-codecs/src/main/kotlin/space/blokk/world/block/codec/AcaciaButtonCodec.kt b/blokk-block-codecs/src/main/kotlin/space/blokk/world/block/codec/AcaciaButtonCodec.kt new file mode 100644 index 0000000..3d5f9b1 --- /dev/null +++ b/blokk-block-codecs/src/main/kotlin/space/blokk/world/block/codec/AcaciaButtonCodec.kt @@ -0,0 +1,4 @@ +package space.blokk.world.block.codec + +class AcaciaButtonCodec { +} diff --git a/blokk-server/src/main/kotlin/space/blokk/BlokkServer.kt b/blokk-server/src/main/kotlin/space/blokk/BlokkServer.kt index bd71593..3eaafe5 100644 --- a/blokk-server/src/main/kotlin/space/blokk/BlokkServer.kt +++ b/blokk-server/src/main/kotlin/space/blokk/BlokkServer.kt @@ -16,6 +16,7 @@ import space.blokk.server.Server import space.blokk.server.event.ServerEvent import space.blokk.util.EncryptionUtils import space.blokk.util.Ticker +import space.blokk.world.block.Hopper import java.io.File import java.security.KeyPair import kotlin.system.exitProcess @@ -94,6 +95,10 @@ class BlokkServer internal constructor() : Server { logger info "Listening on ${config.host}:${config.port}" ticker.start() + + Hopper.codec.states.forEach { + println(it.map { it.toString() }) + } } override fun shutdown() { diff --git a/buildSrc/src/main/kotlin/space/blokk/mdsp/FilesGenerator.kt b/buildSrc/src/main/kotlin/space/blokk/mdsp/FilesGenerator.kt index bca88fe..70ed9d3 100644 --- a/buildSrc/src/main/kotlin/space/blokk/mdsp/FilesGenerator.kt +++ b/buildSrc/src/main/kotlin/space/blokk/mdsp/FilesGenerator.kt @@ -11,11 +11,15 @@ typealias JsonAny = com.jsoniter.any.Any class FilesGenerator(private val dataDir: File, private val outputDir: File, private val sourcesDir: File) { companion object { + const val IMPORT_FOR_REMOVAL_PACKAGE_NAME = "IMPORT_FOR_REMOVAL" + const val PROPERTY_TYPE_FOR_REMOVAL_NAME = "PROPERTY_TYPE_FOR_REMOVAL" + const val API_PACKAGE = "space.blokk" 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 BLOCK_CODEC_TYPE = BLOCK_TYPE.nestedClass("Codec") val K_CLASS_TYPE = ClassName("kotlin.reflect", "KClass") val MAP_TYPE = ClassName("kotlin.collections", "Map") } @@ -74,11 +78,11 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File, pri 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 filePathRelativeToSourceRoot = "./${BLOCK_PACKAGE.replace(".", "/")}/$upperCamelName.kt" + if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue val type = TypeSpec.classBuilder(upperCamelName) - .addKdoc("A block of type [$upperUnderscoreName][%T]", specificMaterialType) + .addKdoc("Material: [$upperUnderscoreName][%T]", specificMaterialType) .superclass(BLOCK_TYPE) .primaryConstructor( FunSpec.constructorBuilder() @@ -86,13 +90,41 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File, pri .build() ) .addSuperclassConstructorParameter("ref") + .addType( + TypeSpec.companionObjectBuilder() + .superclass(BLOCK_TYPE.nestedClass("Companion")) + .addProperty( + PropertySpec.builder( + "codec", + ClassName(IMPORT_FOR_REMOVAL_PACKAGE_NAME, PROPERTY_TYPE_FOR_REMOVAL_NAME), + KModifier.OVERRIDE + ) + .initializer( + """ + %T + .id<${upperCamelName}>(${block.get("id").toInt()}, ${ + block.get("minStateId").toInt() + }) + // .attribute(${upperCamelName}::PROPERTY) + .build() + """.trimIndent(), BLOCK_CODEC_TYPE + ) + .build() + ) + .build() + ) .build() - FileSpec.builder(BLOCK_PACKAGE, upperCamelName) + val fileContent = 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) + .toString() + .replaceFirst("\n\nimport $IMPORT_FOR_REMOVAL_PACKAGE_NAME.$PROPERTY_TYPE_FOR_REMOVAL_NAME", "") + .replaceFirst(": $PROPERTY_TYPE_FOR_REMOVAL_NAME", "") + .replaceFirst("Block.Codec", "Codec") + + outputDir.resolve(filePathRelativeToSourceRoot).writeText(fileContent) } }