Archived
1
0
Fork 0

Combine the material enum with block meta

This commit is contained in:
Moritz Ruth 2020-12-15 21:37:26 +01:00
parent c4b4465d51
commit b96afbfcea
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
14 changed files with 135 additions and 122 deletions

View file

@ -4,7 +4,7 @@
<option name="executionName" /> <option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" /> <option name="scriptParameters" value="--stacktrace" />
<option name="taskDescriptions"> <option name="taskDescriptions">
<list /> <list />
</option> </option>

View file

@ -10,7 +10,7 @@ class Tag(val name: String, val type: Type, val rawValues: List<String>) {
} }
val numericIDs: List<Int> = when (type) { val numericIDs: List<Int> = when (type) {
Type.BLOCKS -> values.map { Material.byID.getValue(it).numericID } Type.BLOCKS -> values.map { Material.byID.getValue(it).codec.id }
else -> TODO() else -> TODO()
} }

View file

@ -1,5 +1,5 @@
package space.blokk.tags package space.blokk.tags
object TagRegistry { object TagRegistry {
val tags: Map<String, Tag> = getTags() val tags: Map<String, Tag> = MINECRAFT_INTERNAL_TAGS
} }

View file

@ -1,6 +1,17 @@
package space.blokk.world.block package space.blokk.world.block
import space.blokk.NamespacedID
/** /**
* Material: [AIR][Material.AIR] * Material: [AIR][Material.AIR]
*/ */
object Air : Block(), Block.Meta<Air> by meta(0, 0) object Air : Block(), Material<Air> by material(
0,
NamespacedID("minecraft:air"),
0,
0f,
true,
0,
0,
BLOCK_COLLISION_SHAPES[0]
)

View file

@ -1,5 +1,6 @@
package space.blokk.world.block package space.blokk.world.block
import space.blokk.NamespacedID
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -8,23 +9,31 @@ import kotlin.reflect.KClass
* Blocks should always be **immutable**. * Blocks should always be **immutable**.
*/ */
abstract class Block internal constructor() { abstract class Block internal constructor() {
/**
* The [Material] of this block.
*/
val material: Material get() = Material.byClass.getValue(this::class)
interface Meta<T : Block> {
val blockClass: KClass<T>
val codec: BlockCodec<T>
}
companion object { companion object {
/** /**
* See [DaylightDetector] for an example on how this function should be used. * See [DaylightDetector] for an example on how this function should be used.
*/ */
internal inline fun <reified T : Block> meta(id: Int, firstStateID: Int) = object : Meta<T> { internal inline fun <reified T : Block> material(
numericID: Int,
id: NamespacedID,
firstStateID: Int,
hardness: Float,
transparent: Boolean,
filteredLight: Int,
emittedLight: Int,
collisionShape: CollisionShape
) = object : Material<T> {
override val blockClass: KClass<T> = T::class override val blockClass: KClass<T> = T::class
override val codec: BlockCodec<T> = BlockCodec(blockClass, id, firstStateID) override val codec: BlockCodec<T> = 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 : Block> T.material(): Material<T> = Material.byClass(this::class) as Material<T>

View file

@ -44,7 +44,7 @@ class BlockCodec<T : Block> internal constructor(
} }
// If this happens, we did something wrong with adding the attributes or with clamping integer attributes // 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 return firstStateID + index
} }

View file

@ -1,6 +1,17 @@
package space.blokk.world.block package space.blokk.world.block
import space.blokk.NamespacedID
/** /**
* Material: [CAVE_AIR][Material.CAVE_AIR] * Material: [CAVE_AIR][Material.CAVE_AIR]
*/ */
object CaveAir : Block(), Block.Meta<CaveAir> by meta(617, 9130) object CaveAir : Block(), Material<CaveAir> by material(
617,
NamespacedID("minecraft:cave_air"),
9130,
0f,
true,
0,
0,
BLOCK_COLLISION_SHAPES[0]
)

View file

@ -1,5 +1,7 @@
package space.blokk.world.block package space.blokk.world.block
import space.blokk.NamespacedID
/** /**
* Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR] * Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR]
*/ */
@ -9,5 +11,14 @@ data class DaylightDetector(
@Attribute(15) @Attribute(15)
val power: Int val power: Int
) : Block() { ) : Block() {
companion object : Meta<DaylightDetector> by meta(1, 6158) companion object : Material<DaylightDetector> by material(
1,
NamespacedID("minecraft:daylight_detector"),
6158,
0.2f,
true,
0,
0,
BLOCK_COLLISION_SHAPES[60]
)
} }

View file

@ -0,0 +1,26 @@
package space.blokk.world.block
import space.blokk.NamespacedID
import kotlin.reflect.KClass
interface Material<T : Block> {
val blockClass: KClass<T>
val codec: BlockCodec<T>
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 <T: Block> byClass(blockClass: KClass<T>): Material<T>? = byClass[blockClass] as Material<T>?
inline fun <reified T: Block> byClass(): Material<T>? = byClass(T::class)
}
}

View file

@ -12,7 +12,7 @@ import space.blokk.world.block.Material
object ChunkDataPacketCodec : OutgoingPacketCodec<ChunkDataPacket>(0x22, ChunkDataPacket::class) { object ChunkDataPacketCodec : OutgoingPacketCodec<ChunkDataPacket>(0x22, ChunkDataPacket::class) {
private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class) private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class)
private val solidHeightmapIgnoredBlocks = Material.values().filter { private val solidHeightmapIgnoredBlocks = Material.all.filter {
// TODO // TODO
true true
} }

View file

@ -2,8 +2,9 @@ package space.blokk.util
import space.blokk.world.Chunk import space.blokk.world.Chunk
import space.blokk.world.block.Block import space.blokk.world.block.Block
import space.blokk.world.block.Material
fun generateHeightmap(sections: Array<Array<Block>?>, ignoredBlocks: Iterable<Block.Meta<*>>): ByteArray { fun generateHeightmap(sections: Array<Array<Block>?>, ignoredBlocks: Iterable<Material<*>>): ByteArray {
val array = ByteArray(Chunk.AREA) val array = ByteArray(Chunk.AREA)
for (sectionIndex in sections.indices.reversed()) { for (sectionIndex in sections.indices.reversed()) {

View file

@ -113,6 +113,7 @@ class BlokkScheduler : Scheduler {
override fun registerShutdownTask(block: suspend () -> Unit): Scheduler.Task { override fun registerShutdownTask(block: suspend () -> Unit): Scheduler.Task {
shutdownTasks.add(block) shutdownTasks.add(block)
return object : Scheduler.Task { return object : Scheduler.Task {
override fun cancel() { override fun cancel() {
shutdownTasks.remove(block) shutdownTasks.remove(block)

View file

@ -2,6 +2,7 @@ package space.blokk.mdsp.generator
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 space.blokk.mdsp.JsonAny import space.blokk.mdsp.JsonAny
@ -18,9 +19,6 @@ class BlocksAndMaterialGenerator(
val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material") val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material")
val NAMESPACED_ID_TYPE = ClassName("space.blokk", "NamespacedID") val NAMESPACED_ID_TYPE = ClassName("space.blokk", "NamespacedID")
val BLOCK_TYPE = ClassName(BLOCK_PACKAGE, "Block") 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( val BLOCK_BLACKLIST = listOf(
// Only used on the client side for y values lower than 0 and higher than 255 // 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 dataJson = workingDir.resolve("blocks.json").readText()
val blocks = JsonIterator.deserialize(dataJson).asList() val blocks = JsonIterator.deserialize(dataJson).asList()
generateBlockStubs(blocks) generateBlockStubs(blocks, collisionShapesData.get("blocks").asMap())
generateMaterialEnum(blocks) generateBlocksList(blocks)
} }
private fun generateCollisionShapes(shapes: Map<String, JsonAny>) { private fun generateCollisionShapes(shapes: Map<String, JsonAny>) {
@ -54,12 +52,12 @@ class BlocksAndMaterialGenerator(
val typeAlias = TypeAliasSpec.builder( val typeAlias = TypeAliasSpec.builder(
"CollisionShape", "CollisionShape",
ARRAY_TYPE.parameterizedBy(ARRAY_TYPE.parameterizedBy(ClassName("kotlin", "Float"))) Array::class.asTypeName().parameterizedBy(Array::class.asTypeName().parameterizedBy(ClassName("kotlin", "Float")))
).build() ).build()
val property = PropertySpec.builder( val property = PropertySpec.builder(
"BLOCK_COLLISION_SHAPES", "BLOCK_COLLISION_SHAPES",
ARRAY_TYPE.parameterizedBy(ClassName(BLOCK_PACKAGE, "CollisionShape")) Array::class.asTypeName().parameterizedBy(ClassName(BLOCK_PACKAGE, "CollisionShape"))
) )
.initializer("arrayOf(${items.joinToString()})") .initializer("arrayOf(${items.joinToString()})")
.addKdoc( .addKdoc(
@ -75,18 +73,30 @@ class BlocksAndMaterialGenerator(
.writeTo(outputDir) .writeTo(outputDir)
} }
private fun generateBlockStubs(blocks: List<JsonAny>) { private fun generateBlockStubs(blocks: List<JsonAny>, collisionShapes: Map<String, JsonAny>) {
for (block in blocks) { for (block in blocks) {
val lowerUnderscoreName = block.get("name").toString() val lowerUnderscoreName = block.get("name").toString()
if (BLOCK_BLACKLIST.contains(lowerUnderscoreName)) continue if (BLOCK_BLACKLIST.contains(lowerUnderscoreName)) continue
val upperCamelName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, lowerUnderscoreName) 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" val filePathRelativeToSourceRoot = "./${BLOCK_PACKAGE.replace(".", "/")}/$upperCamelName.kt"
if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue 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) val type = TypeSpec.classBuilder(upperCamelName)
.apply { .apply {
if (block.get("states").asList().isNotEmpty()) { if (block.get("states").asList().isNotEmpty()) {
@ -97,13 +107,12 @@ class BlocksAndMaterialGenerator(
addProperty(PropertySpec.builder("PLACEHOLDER", Unit::class).initializer("PLACEHOLDER").build()) addProperty(PropertySpec.builder("PLACEHOLDER", Unit::class).initializer("PLACEHOLDER").build())
} }
} }
.addKdoc("Material: [$upperUnderscoreName][%T]", specificMaterialType)
.superclass(BLOCK_TYPE) .superclass(BLOCK_TYPE)
.addType( .addType(
TypeSpec.companionObjectBuilder() TypeSpec.companionObjectBuilder()
.addSuperinterface( .addSuperinterface(
BLOCK_TYPE.nestedClass("Meta").parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)), MATERIAL_TYPE.parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)),
CodeBlock.of("meta(${block.get("id").toInt()}, ${block.get("minStateId").toInt()})") CodeBlock.of("material(\n${metaLines.joinToString(",\n") { " $it" }}\n)", *metaArgs)
) )
.build() .build()
) )
@ -120,86 +129,21 @@ class BlocksAndMaterialGenerator(
} }
} }
private fun generateMaterialEnum(blocks: List<JsonAny>) { private fun generateBlocksList(blocks: List<JsonAny>) {
val cph = ConstructorPropertiesHelper() 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") val property = PropertySpec.builder(
.primaryConstructor( "GENERATED_BLOCKS",
FunSpec.constructorBuilder() List::class.asTypeName().parameterizedBy(MATERIAL_TYPE.parameterizedBy(STAR))
.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()
) )
.initializer("listOf(\n${names.joinToString(",\n")}\n)")
.build()
FileSpec.builder(BLOCK_PACKAGE, "Material") FileSpec.builder(BLOCK_PACKAGE, "Blocks")
.addType(builder.build()) .addProperty(property)
.build() .build()
.writeTo(outputDir) .writeTo(outputDir)
} }

View file

@ -1,26 +1,25 @@
package space.blokk.mdsp.generator package space.blokk.mdsp.generator
import com.jsoniter.JsonIterator import com.jsoniter.JsonIterator
import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import java.io.File import java.io.File
class TagsFileGenerator(val workingDir: File, val outputDir: File) { class TagsFileGenerator(private val workingDir: File, private val outputDir: File) {
companion object { companion object {
const val TAGS_PACKAGE = "space.blokk.tags" const val TAGS_PACKAGE = "space.blokk.tags"
val TAG_TYPE = ClassName(TAGS_PACKAGE, "Tag") 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( val TAG_TYPE_TYPES_BY_DIR_NAME = mapOf(
"blocks" to TAG_TYPE_TYPE.nestedClass("BLOCKS"), "blocks" to TAG_TYPE_TYPE.nestedClass("BLOCKS"),
"entity_types" to TAG_TYPE_TYPE.nestedClass("ENTITY_TYPES"), "entity_types" to TAG_TYPE_TYPE.nestedClass("ENTITY_TYPES"),
"fluids" to TAG_TYPE_TYPE.nestedClass("FLUIDS"), "fluids" to TAG_TYPE_TYPE.nestedClass("FLUIDS"),
"items" to TAG_TYPE_TYPE.nestedClass("ITEMS") "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() { fun generate() {
val entries: List<Pair<String, Array<Any>>> = TAG_TYPE_TYPES_BY_DIR_NAME.flatMap { (dirName, tagTypeType) -> val entries: List<Pair<String, Array<Any>>> = 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") val property = PropertySpec.builder("MINECRAFT_INTERNAL_TAGS", Map::class.asTypeName().parameterizedBy(String::class.asTypeName(), TAG_TYPE))
.addStatement( .initializer(
"return mapOf(\n${entries.joinToString(",\n") { it.first.prependIndent(" ") }}\n)", "mapOf(\n${entries.joinToString(",\n") { it.first.prependIndent(" ") }}\n)",
*entries.flatMap { it.second.toList() }.toTypedArray() *entries.flatMap { it.second.toList() }.toTypedArray()
) )
.addModifiers(KModifier.INTERNAL) .addModifiers(KModifier.INTERNAL)
.build() .build()
FileSpec.builder(TAGS_PACKAGE, "Tags") FileSpec.builder(TAGS_PACKAGE, "Tags")
.addFunction(function) .addProperty(property)
.build() .build()
.writeTo(outputDir) .writeTo(outputDir)
} }