diff --git a/.runConfigurations/Generate.run.xml b/.runConfigurations/Generate.run.xml index 1907bf1..3f6e341 100644 --- a/.runConfigurations/Generate.run.xml +++ b/.runConfigurations/Generate.run.xml @@ -4,7 +4,7 @@ diff --git a/blokk-api/src/main/kotlin/space/blokk/tags/Tag.kt b/blokk-api/src/main/kotlin/space/blokk/tags/Tag.kt index d1b72c1..78957e1 100644 --- a/blokk-api/src/main/kotlin/space/blokk/tags/Tag.kt +++ b/blokk-api/src/main/kotlin/space/blokk/tags/Tag.kt @@ -10,7 +10,7 @@ class Tag(val name: String, val type: Type, val rawValues: List) { } val numericIDs: List = when (type) { - Type.BLOCKS -> values.map { Material.byID.getValue(it).numericID } + Type.BLOCKS -> values.map { Material.byID.getValue(it).codec.id } else -> TODO() } diff --git a/blokk-api/src/main/kotlin/space/blokk/tags/TagRegistry.kt b/blokk-api/src/main/kotlin/space/blokk/tags/TagRegistry.kt index 4615b28..4fe291e 100644 --- a/blokk-api/src/main/kotlin/space/blokk/tags/TagRegistry.kt +++ b/blokk-api/src/main/kotlin/space/blokk/tags/TagRegistry.kt @@ -1,5 +1,5 @@ package space.blokk.tags object TagRegistry { - val tags: Map = getTags() + val tags: Map = MINECRAFT_INTERNAL_TAGS } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/Air.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Air.kt index 27749e9..a9478b5 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/Air.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Air.kt @@ -1,6 +1,17 @@ package space.blokk.world.block +import space.blokk.NamespacedID + /** * Material: [AIR][Material.AIR] */ -object Air : Block(), Block.Meta by meta(0, 0) +object Air : Block(), Material by material( + 0, + NamespacedID("minecraft:air"), + 0, + 0f, + true, + 0, + 0, + BLOCK_COLLISION_SHAPES[0] +) 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 c885fa1..2df2534 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,6 @@ package space.blokk.world.block +import space.blokk.NamespacedID import kotlin.reflect.KClass /** @@ -8,23 +9,31 @@ import kotlin.reflect.KClass * Blocks should always be **immutable**. */ abstract class Block internal constructor() { - /** - * The [Material] of this block. - */ - val material: Material get() = Material.byClass.getValue(this::class) - - interface Meta { - val blockClass: KClass - val codec: BlockCodec - } - companion object { /** * See [DaylightDetector] for an example on how this function should be used. */ - internal inline fun meta(id: Int, firstStateID: Int) = object : Meta { + internal inline fun material( + numericID: Int, + id: NamespacedID, + firstStateID: Int, + hardness: Float, + transparent: Boolean, + filteredLight: Int, + emittedLight: Int, + collisionShape: CollisionShape + ) = object : Material { override val blockClass: KClass = T::class - override val codec: BlockCodec = BlockCodec(blockClass, id, firstStateID) + override val codec: BlockCodec = BlockCodec(blockClass, numericID, firstStateID) + override val id: NamespacedID = id + override val hardness: Float = hardness + override val transparent: Boolean = transparent + override val filteredLight: Int = filteredLight + override val emittedLight: Int = emittedLight + override val collisionShape: CollisionShape = collisionShape } } } + +@Suppress("UNCHECKED_CAST") +fun T.material(): Material = Material.byClass(this::class) as Material diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt index 405805d..4ebef1d 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt @@ -44,7 +44,7 @@ class BlockCodec internal constructor( } // If this happens, we did something wrong with adding the attributes or with clamping integer attributes - if (index == -1) throw Error("The state of block (${block.material}) has no ID") + if (index == -1) throw Error("The state of block (${block.material()!!.id}) has no ID") return firstStateID + index } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/CaveAir.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/CaveAir.kt index a5be3b8..898c011 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/CaveAir.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/CaveAir.kt @@ -1,6 +1,17 @@ package space.blokk.world.block +import space.blokk.NamespacedID + /** * Material: [CAVE_AIR][Material.CAVE_AIR] */ -object CaveAir : Block(), Block.Meta by meta(617, 9130) +object CaveAir : Block(), Material by material( + 617, + NamespacedID("minecraft:cave_air"), + 9130, + 0f, + true, + 0, + 0, + BLOCK_COLLISION_SHAPES[0] +) diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/DaylightDetector.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/DaylightDetector.kt index bfdbda3..9fe7228 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/block/DaylightDetector.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/DaylightDetector.kt @@ -1,5 +1,7 @@ package space.blokk.world.block +import space.blokk.NamespacedID + /** * Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR] */ @@ -9,5 +11,14 @@ data class DaylightDetector( @Attribute(15) val power: Int ) : Block() { - companion object : Meta by meta(1, 6158) + companion object : Material by material( + 1, + NamespacedID("minecraft:daylight_detector"), + 6158, + 0.2f, + true, + 0, + 0, + BLOCK_COLLISION_SHAPES[60] + ) } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt new file mode 100644 index 0000000..9a4ff1a --- /dev/null +++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt @@ -0,0 +1,26 @@ +package space.blokk.world.block + +import space.blokk.NamespacedID +import kotlin.reflect.KClass + +interface Material { + val blockClass: KClass + val codec: BlockCodec + val id: NamespacedID + val hardness: Float + val transparent: Boolean + val filteredLight: Int + val emittedLight: Int + val collisionShape: CollisionShape + + companion object { + val all = GENERATED_BLOCKS + val byID = GENERATED_BLOCKS.map { it.id to it }.toMap() + + private val byClass = GENERATED_BLOCKS.map { it.blockClass to it }.toMap() + + @Suppress("UNCHECKED_CAST") + fun byClass(blockClass: KClass): Material? = byClass[blockClass] as Material? + inline fun byClass(): Material? = byClass(T::class) + } +} diff --git a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt index ef8d773..53469ca 100644 --- a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt +++ b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt @@ -12,7 +12,7 @@ import space.blokk.world.block.Material object ChunkDataPacketCodec : OutgoingPacketCodec(0x22, ChunkDataPacket::class) { private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class) - private val solidHeightmapIgnoredBlocks = Material.values().filter { + private val solidHeightmapIgnoredBlocks = Material.all.filter { // TODO true } diff --git a/blokk-packet-codecs/src/main/kotlin/space/blokk/util/GenerateHeightmap.kt b/blokk-packet-codecs/src/main/kotlin/space/blokk/util/GenerateHeightmap.kt index 8795a45..c6a1ab9 100644 --- a/blokk-packet-codecs/src/main/kotlin/space/blokk/util/GenerateHeightmap.kt +++ b/blokk-packet-codecs/src/main/kotlin/space/blokk/util/GenerateHeightmap.kt @@ -2,8 +2,9 @@ package space.blokk.util import space.blokk.world.Chunk import space.blokk.world.block.Block +import space.blokk.world.block.Material -fun generateHeightmap(sections: Array?>, ignoredBlocks: Iterable>): ByteArray { +fun generateHeightmap(sections: Array?>, ignoredBlocks: Iterable>): ByteArray { val array = ByteArray(Chunk.AREA) for (sectionIndex in sections.indices.reversed()) { diff --git a/blokk-server/src/main/kotlin/space/blokk/BlokkScheduler.kt b/blokk-server/src/main/kotlin/space/blokk/BlokkScheduler.kt index e273aba..05c24ed 100644 --- a/blokk-server/src/main/kotlin/space/blokk/BlokkScheduler.kt +++ b/blokk-server/src/main/kotlin/space/blokk/BlokkScheduler.kt @@ -113,6 +113,7 @@ class BlokkScheduler : Scheduler { override fun registerShutdownTask(block: suspend () -> Unit): Scheduler.Task { shutdownTasks.add(block) + return object : Scheduler.Task { override fun cancel() { shutdownTasks.remove(block) diff --git a/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/BlocksAndMaterialGenerator.kt b/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/BlocksAndMaterialGenerator.kt index e989693..fb7a0f4 100644 --- a/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/BlocksAndMaterialGenerator.kt +++ b/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/BlocksAndMaterialGenerator.kt @@ -2,6 +2,7 @@ package space.blokk.mdsp.generator import com.google.common.base.CaseFormat import com.jsoniter.JsonIterator +import com.jsoniter.any.Any import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import space.blokk.mdsp.JsonAny @@ -18,9 +19,6 @@ class BlocksAndMaterialGenerator( val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material") val NAMESPACED_ID_TYPE = ClassName("space.blokk", "NamespacedID") val BLOCK_TYPE = ClassName(BLOCK_PACKAGE, "Block") - val K_CLASS_TYPE = ClassName("kotlin.reflect", "KClass") - val MAP_TYPE = ClassName("kotlin.collections", "Map") - val ARRAY_TYPE = ClassName("kotlin", "Array") val BLOCK_BLACKLIST = listOf( // Only used on the client side for y values lower than 0 and higher than 255 @@ -39,8 +37,8 @@ class BlocksAndMaterialGenerator( val dataJson = workingDir.resolve("blocks.json").readText() val blocks = JsonIterator.deserialize(dataJson).asList() - generateBlockStubs(blocks) - generateMaterialEnum(blocks) + generateBlockStubs(blocks, collisionShapesData.get("blocks").asMap()) + generateBlocksList(blocks) } private fun generateCollisionShapes(shapes: Map) { @@ -54,12 +52,12 @@ class BlocksAndMaterialGenerator( val typeAlias = TypeAliasSpec.builder( "CollisionShape", - ARRAY_TYPE.parameterizedBy(ARRAY_TYPE.parameterizedBy(ClassName("kotlin", "Float"))) + Array::class.asTypeName().parameterizedBy(Array::class.asTypeName().parameterizedBy(ClassName("kotlin", "Float"))) ).build() val property = PropertySpec.builder( "BLOCK_COLLISION_SHAPES", - ARRAY_TYPE.parameterizedBy(ClassName(BLOCK_PACKAGE, "CollisionShape")) + Array::class.asTypeName().parameterizedBy(ClassName(BLOCK_PACKAGE, "CollisionShape")) ) .initializer("arrayOf(${items.joinToString()})") .addKdoc( @@ -75,18 +73,30 @@ class BlocksAndMaterialGenerator( .writeTo(outputDir) } - private fun generateBlockStubs(blocks: List) { + private fun generateBlockStubs(blocks: List, collisionShapes: Map) { for (block in blocks) { val lowerUnderscoreName = block.get("name").toString() if (BLOCK_BLACKLIST.contains(lowerUnderscoreName)) continue 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) val filePathRelativeToSourceRoot = "./${BLOCK_PACKAGE.replace(".", "/")}/$upperCamelName.kt" if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue + val metaLines = listOf("%L", "%T(%S)", "%L", "%Lf", "%L", "%L", "%L", "BLOCK_COLLISION_SHAPES[%L]") + + val metaArgs = arrayOf( + block.get("id").toInt(), + NAMESPACED_ID_TYPE, + "minecraft:$lowerUnderscoreName", + block.get("minStateId").toInt(), + block.get("hardness").toFloat(), + block.get("transparent").toBoolean(), + block.get("filterLight").toInt(), + block.get("emitLight").toInt(), + collisionShapes.getValue(lowerUnderscoreName).toInt() + ) + val type = TypeSpec.classBuilder(upperCamelName) .apply { if (block.get("states").asList().isNotEmpty()) { @@ -97,13 +107,12 @@ class BlocksAndMaterialGenerator( addProperty(PropertySpec.builder("PLACEHOLDER", Unit::class).initializer("PLACEHOLDER").build()) } } - .addKdoc("Material: [$upperUnderscoreName][%T]", specificMaterialType) .superclass(BLOCK_TYPE) .addType( TypeSpec.companionObjectBuilder() .addSuperinterface( - BLOCK_TYPE.nestedClass("Meta").parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)), - CodeBlock.of("meta(${block.get("id").toInt()}, ${block.get("minStateId").toInt()})") + MATERIAL_TYPE.parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)), + CodeBlock.of("material(\n${metaLines.joinToString(",\n") { " $it" }}\n)", *metaArgs) ) .build() ) @@ -120,86 +129,21 @@ class BlocksAndMaterialGenerator( } } - private fun generateMaterialEnum(blocks: List) { - val cph = ConstructorPropertiesHelper() + private fun generateBlocksList(blocks: List) { + val names = blocks + .map { it.get("name").toString() } + .filter { !BLOCK_BLACKLIST.contains(it) } + .map { CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, it) } - val builder = TypeSpec.enumBuilder("Material") - .primaryConstructor( - FunSpec.constructorBuilder() - .addParameters( - listOf( - cph.create("numericID", Int::class), - cph.create("id", NAMESPACED_ID_TYPE), - cph.create( - "blockMeta", - BLOCK_TYPE.nestedClass("Meta").parameterizedBy(STAR) - ), - cph.create("hardness", Float::class), - cph.create("transparent", Boolean::class), - cph.create("filteredLight", Int::class), - cph.create("maxStackSize", Byte::class), - ) - ) - .build() - ) - .addProperties(cph.getProperties()) - - for (block in blocks) { - val name = block.get("name").toString() - if (BLOCK_BLACKLIST.contains(name)) continue - - val materialName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, name) - val blockClassName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name) - - builder.addEnumConstant( - materialName, - TypeSpec.anonymousClassBuilder() - .addSuperclassConstructorParameter("%L", block.get("id").toInt()) - .addSuperclassConstructorParameter("%T(%S)", NAMESPACED_ID_TYPE, "minecraft:$name") - .addSuperclassConstructorParameter( - "%T", - ClassName( - BLOCK_PACKAGE, - blockClassName - ) - ) - .addSuperclassConstructorParameter("%Lf", block.get("hardness").toFloat()) - .addSuperclassConstructorParameter("%L", block.get("transparent").toBoolean()) - .addSuperclassConstructorParameter("%L", block.get("filterLight").toInt().toByte()) - .addSuperclassConstructorParameter("%L", block.get("stackSize").toInt()) - .build() - ) - } - - builder.addType( - TypeSpec.companionObjectBuilder() - .addProperty( - PropertySpec.builder( - "byClass", - MAP_TYPE.parameterizedBy( - K_CLASS_TYPE.parameterizedBy(WildcardTypeName.producerOf(BLOCK_TYPE)), - MATERIAL_TYPE - ) - ) - .initializer("values().map { it.blockMeta.blockClass to it }.toMap()") - .build() - ) - .addProperty( - PropertySpec.builder( - "byID", - MAP_TYPE.parameterizedBy( - NAMESPACED_ID_TYPE, - MATERIAL_TYPE - ) - ) - .initializer("values().map { it.id to it }.toMap()") - .build() - ) - .build() + val property = PropertySpec.builder( + "GENERATED_BLOCKS", + List::class.asTypeName().parameterizedBy(MATERIAL_TYPE.parameterizedBy(STAR)) ) + .initializer("listOf(\n${names.joinToString(",\n")}\n)") + .build() - FileSpec.builder(BLOCK_PACKAGE, "Material") - .addType(builder.build()) + FileSpec.builder(BLOCK_PACKAGE, "Blocks") + .addProperty(property) .build() .writeTo(outputDir) } diff --git a/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/TagsFileGenerator.kt b/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/TagsFileGenerator.kt index aeda901..5236088 100644 --- a/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/TagsFileGenerator.kt +++ b/buildSrc/src/main/kotlin/space/blokk/mdsp/generator/TagsFileGenerator.kt @@ -1,26 +1,25 @@ package space.blokk.mdsp.generator import com.jsoniter.JsonIterator -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.FileSpec -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import java.io.File -class TagsFileGenerator(val workingDir: File, val outputDir: File) { +class TagsFileGenerator(private val workingDir: File, private val outputDir: File) { companion object { const val TAGS_PACKAGE = "space.blokk.tags" val TAG_TYPE = ClassName(TAGS_PACKAGE, "Tag") - val TAG_TYPE_TYPE = TAG_TYPE.nestedClass("Type") + private val TAG_TYPE_TYPE = TAG_TYPE.nestedClass("Type") val TAG_TYPE_TYPES_BY_DIR_NAME = mapOf( "blocks" to TAG_TYPE_TYPE.nestedClass("BLOCKS"), "entity_types" to TAG_TYPE_TYPE.nestedClass("ENTITY_TYPES"), "fluids" to TAG_TYPE_TYPE.nestedClass("FLUIDS"), "items" to TAG_TYPE_TYPE.nestedClass("ITEMS") ) + val MAP_TYPE = ClassName("kotlin.collections", "Map") } - val tagsDir = workingDir.resolve("generated/data/minecraft/tags") + private val tagsDir = workingDir.resolve("generated/data/minecraft/tags") fun generate() { val entries: List>> = TAG_TYPE_TYPES_BY_DIR_NAME.flatMap { (dirName, tagTypeType) -> @@ -37,16 +36,16 @@ class TagsFileGenerator(val workingDir: File, val outputDir: File) { } } - val function = FunSpec.builder("getTags") - .addStatement( - "return mapOf(\n${entries.joinToString(",\n") { it.first.prependIndent(" ") }}\n)", + val property = PropertySpec.builder("MINECRAFT_INTERNAL_TAGS", Map::class.asTypeName().parameterizedBy(String::class.asTypeName(), TAG_TYPE)) + .initializer( + "mapOf(\n${entries.joinToString(",\n") { it.first.prependIndent(" ") }}\n)", *entries.flatMap { it.second.toList() }.toTypedArray() ) .addModifiers(KModifier.INTERNAL) .build() FileSpec.builder(TAGS_PACKAGE, "Tags") - .addFunction(function) + .addProperty(property) .build() .writeTo(outputDir) }