Combine the material enum with block meta
This commit is contained in:
parent
c4b4465d51
commit
b96afbfcea
14 changed files with 135 additions and 122 deletions
|
@ -4,7 +4,7 @@
|
|||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="scriptParameters" value="--stacktrace" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
|
|
|
@ -10,7 +10,7 @@ class Tag(val name: String, val type: Type, val rawValues: List<String>) {
|
|||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package space.blokk.tags
|
||||
|
||||
object TagRegistry {
|
||||
val tags: Map<String, Tag> = getTags()
|
||||
val tags: Map<String, Tag> = MINECRAFT_INTERNAL_TAGS
|
||||
}
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
package space.blokk.world.block
|
||||
|
||||
import space.blokk.NamespacedID
|
||||
|
||||
/**
|
||||
* 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]
|
||||
)
|
||||
|
|
|
@ -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<T : Block> {
|
||||
val blockClass: KClass<T>
|
||||
val codec: BlockCodec<T>
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 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 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>
|
||||
|
|
|
@ -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 (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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
package space.blokk.world.block
|
||||
|
||||
import space.blokk.NamespacedID
|
||||
|
||||
/**
|
||||
* 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]
|
||||
)
|
||||
|
|
|
@ -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<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]
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import space.blokk.world.block.Material
|
|||
|
||||
object ChunkDataPacketCodec : OutgoingPacketCodec<ChunkDataPacket>(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
|
||||
}
|
||||
|
|
|
@ -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<Array<Block>?>, ignoredBlocks: Iterable<Block.Meta<*>>): ByteArray {
|
||||
fun generateHeightmap(sections: Array<Array<Block>?>, ignoredBlocks: Iterable<Material<*>>): ByteArray {
|
||||
val array = ByteArray(Chunk.AREA)
|
||||
|
||||
for (sectionIndex in sections.indices.reversed()) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<String, JsonAny>) {
|
||||
|
@ -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<JsonAny>) {
|
||||
private fun generateBlockStubs(blocks: List<JsonAny>, collisionShapes: Map<String, JsonAny>) {
|
||||
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<JsonAny>) {
|
||||
val cph = ConstructorPropertiesHelper()
|
||||
private fun generateBlocksList(blocks: List<JsonAny>) {
|
||||
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),
|
||||
)
|
||||
val property = PropertySpec.builder(
|
||||
"GENERATED_BLOCKS",
|
||||
List::class.asTypeName().parameterizedBy(MATERIAL_TYPE.parameterizedBy(STAR))
|
||||
)
|
||||
.initializer("listOf(\n${names.joinToString(",\n")}\n)")
|
||||
.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()
|
||||
)
|
||||
|
||||
FileSpec.builder(BLOCK_PACKAGE, "Material")
|
||||
.addType(builder.build())
|
||||
FileSpec.builder(BLOCK_PACKAGE, "Blocks")
|
||||
.addProperty(property)
|
||||
.build()
|
||||
.writeTo(outputDir)
|
||||
}
|
||||
|
|
|
@ -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<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")
|
||||
.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)
|
||||
}
|
||||
|
|
Reference in a new issue