Rename BlockRef to voxel and use annotations instead of BlockCodec.Builder
This commit is contained in:
parent
9f97b737c0
commit
dcbad8f5dc
12 changed files with 176 additions and 252 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("kapt")
|
kotlin("kapt")
|
||||||
|
@ -60,3 +62,4 @@ tasks {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val compileKotlin: KotlinCompile by tasks
|
||||||
|
|
|
@ -2,7 +2,6 @@ package space.blokk.world
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import space.blokk.world.Chunk.Key
|
import space.blokk.world.Chunk.Key
|
||||||
import space.blokk.world.block.BlockRef
|
|
||||||
|
|
||||||
abstract class Chunk(
|
abstract class Chunk(
|
||||||
/**
|
/**
|
||||||
|
@ -35,11 +34,11 @@ abstract class Chunk(
|
||||||
abstract val sections: Array<ChunkSection>
|
abstract val sections: Array<ChunkSection>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the [BlockRef] for the specified coordinates in this chunk.
|
* Returns the [Voxel] 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.**
|
||||||
*/
|
*/
|
||||||
abstract fun getBlock(x: Byte, y: Int, z: Byte): BlockRef
|
abstract fun getBlock(x: Byte, y: Int, z: Byte): Voxel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads this chunk into memory.
|
* Loads this chunk into memory.
|
||||||
|
|
44
blokk-api/src/main/kotlin/space/blokk/world/Voxel.kt
Normal file
44
blokk-api/src/main/kotlin/space/blokk/world/Voxel.kt
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package space.blokk.world
|
||||||
|
|
||||||
|
import space.blokk.world.block.Block
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A voxel in a chunk of a world.
|
||||||
|
*
|
||||||
|
* There is always only one instance for every [location][BlockLocation] in a world.
|
||||||
|
*/
|
||||||
|
abstract class Voxel {
|
||||||
|
/**
|
||||||
|
* The [location][BlockLocation] of this voxel.
|
||||||
|
*/
|
||||||
|
abstract val location: BlockLocation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [World] containing this voxel.
|
||||||
|
*/
|
||||||
|
abstract val world: World
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Chunk] containing this voxel.
|
||||||
|
*/
|
||||||
|
val chunk: Chunk get() = world.getChunkAt(location)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current content ([Block]) of this voxel.
|
||||||
|
*/
|
||||||
|
abstract var block: Block
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand for [chunk.loaded][Chunk.loaded].
|
||||||
|
*/
|
||||||
|
val loaded: Boolean get() = chunk.loaded
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand for [`chunk.load()`][Chunk.load].
|
||||||
|
*/
|
||||||
|
suspend fun load() = chunk.load()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun of(world: World, location: BlockLocation) = world.getBlock(location)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,14 +6,13 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import space.blokk.CoordinatePartOrder
|
import space.blokk.CoordinatePartOrder
|
||||||
import space.blokk.entity.Entity
|
import space.blokk.entity.Entity
|
||||||
import space.blokk.world.block.BlockRef
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* **Note: Although the methods in this class are called `getBlock`, `getBlocksInSphere` and so on, they actually
|
||||||
* return [BlockRef]s.**
|
* return [Voxel]s.**
|
||||||
*/
|
*/
|
||||||
abstract class World(val uuid: UUID) {
|
abstract class World(val uuid: UUID) {
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +61,7 @@ abstract class World(val uuid: UUID) {
|
||||||
/**
|
/**
|
||||||
* @return The block at [location].
|
* @return The block at [location].
|
||||||
*/
|
*/
|
||||||
abstract fun getBlock(location: BlockLocation): BlockRef
|
abstract fun getBlock(location: BlockLocation): Voxel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param order The order of the arrays.
|
* @param order The order of the arrays.
|
||||||
|
@ -73,7 +72,7 @@ abstract class World(val uuid: UUID) {
|
||||||
firstCorner: BlockLocation,
|
firstCorner: BlockLocation,
|
||||||
secondCorner: BlockLocation,
|
secondCorner: BlockLocation,
|
||||||
order: CoordinatePartOrder = CoordinatePartOrder.DEFAULT
|
order: CoordinatePartOrder = CoordinatePartOrder.DEFAULT
|
||||||
): Array<Array<Array<BlockRef>>> {
|
): Array<Array<Array<Voxel>>> {
|
||||||
val start = firstCorner.withLowestValues(secondCorner)
|
val start = firstCorner.withLowestValues(secondCorner)
|
||||||
val end = firstCorner.withHighestValues(secondCorner)
|
val end = firstCorner.withHighestValues(secondCorner)
|
||||||
|
|
||||||
|
@ -92,7 +91,7 @@ abstract class World(val uuid: UUID) {
|
||||||
fun getBlocksInSphere(
|
fun getBlocksInSphere(
|
||||||
center: BlockLocation,
|
center: BlockLocation,
|
||||||
radius: Int
|
radius: Int
|
||||||
): Array<BlockRef> {
|
): Array<Voxel> {
|
||||||
// https://www.reddit.com/r/VoxelGameDev/comments/2cttnt/how_to_create_a_sphere_out_of_voxels/
|
// https://www.reddit.com/r/VoxelGameDev/comments/2cttnt/how_to_create_a_sphere_out_of_voxels/
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package space.blokk.world.block
|
||||||
|
|
||||||
|
@Retention
|
||||||
|
@Target(AnnotationTarget.PROPERTY)
|
||||||
|
annotation class Attribute(
|
||||||
|
/**
|
||||||
|
* The maximum allowed value of this attribute.
|
||||||
|
* Silently ignored for properties whose type is not [Int].
|
||||||
|
*/
|
||||||
|
val max: Int = 0
|
||||||
|
)
|
|
@ -1,121 +1,25 @@
|
||||||
package space.blokk.world.block
|
package space.blokk.world.block
|
||||||
|
|
||||||
import space.blokk.util.KPropertyValuePair
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ref The [BlockRef] referencing this block.
|
* Child classes should never have mutable properties.
|
||||||
*/
|
*/
|
||||||
abstract class Block internal constructor(val ref: BlockRef) {
|
abstract class Block internal constructor() {
|
||||||
/**
|
/**
|
||||||
* The [Material] of this block.
|
* The [Material] of this block.
|
||||||
*/
|
*/
|
||||||
val material: Material get() = Material.byClass.getValue(this::class)
|
val material: Material get() = Material.byClass.getValue(this::class)
|
||||||
|
|
||||||
/**
|
interface Meta<T : Block> {
|
||||||
* Whether [ref] still references this block.
|
val blockClass: KClass<T>
|
||||||
*/
|
val codec: BlockCodec<T>
|
||||||
val destroyed: Boolean get() = ref.block === this
|
|
||||||
|
|
||||||
protected fun throwIfDestroyed() {
|
|
||||||
if (destroyed) throw IllegalStateException("The block was destroyed.")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when [ref] no longer references this block.
|
|
||||||
*/
|
|
||||||
protected open fun onDestroy() {}
|
|
||||||
|
|
||||||
internal fun destroy() {
|
|
||||||
onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Companion {
|
|
||||||
abstract val codec: Codec<*>
|
|
||||||
}
|
|
||||||
|
|
||||||
class Codec<T : Block> private constructor(
|
|
||||||
val blockClass: KClass<out T>,
|
|
||||||
val id: Int,
|
|
||||||
val firstStateID: Int,
|
|
||||||
val states: List<Array<KPropertyValuePair<*>>>
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
inline fun <reified T : Block> id(id: Int, firstStateID: Int) = id(T::class, id, firstStateID)
|
|
||||||
fun <T : Block> id(blockClass: KClass<out T>, id: Int, firstStateID: Int) =
|
|
||||||
Builder(blockClass, id, firstStateID)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getStateID(block: T): Int {
|
|
||||||
val values = mutableMapOf<KProperty<*>, Any>()
|
|
||||||
|
|
||||||
val index = states.indexOfFirst { propertyValuePairs ->
|
|
||||||
propertyValuePairs.all {
|
|
||||||
values.getOrPut(it.property) { it.property.call(block)!! } == it.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
|
|
||||||
return firstStateID + index
|
|
||||||
}
|
|
||||||
|
|
||||||
class Builder<T : Block> internal constructor(
|
|
||||||
private val blockClass: KClass<out T>,
|
|
||||||
private val id: Int,
|
|
||||||
private val firstStateID: Int
|
|
||||||
) {
|
|
||||||
private val attributes = mutableListOf<BlockAttribute<*>>()
|
|
||||||
|
|
||||||
@JvmName("booleanAttribute")
|
|
||||||
fun attribute(property: KProperty<Boolean>) = this.apply {
|
|
||||||
attributes.add(BlockAttribute.Boolean(property))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("enumAttribute")
|
|
||||||
fun attribute(property: KProperty<Enum<*>>) = this.apply {
|
|
||||||
attributes.add(BlockAttribute.Enum(property))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("intAttribute")
|
|
||||||
fun attribute(property: KProperty<Int>, max: Int) = this.apply {
|
|
||||||
attributes.add(BlockAttribute.Int(property, max))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build() = Codec(blockClass, id, firstStateID, generateStates(attributes))
|
|
||||||
|
|
||||||
sealed class BlockAttribute<T : Any> {
|
|
||||||
abstract val property: KProperty<T>
|
|
||||||
|
|
||||||
data class Boolean(override val property: KProperty<kotlin.Boolean>) : BlockAttribute<kotlin.Boolean>()
|
|
||||||
data class Int(override val property: KProperty<kotlin.Int>, val max: kotlin.Int) :
|
|
||||||
BlockAttribute<kotlin.Int>()
|
|
||||||
|
|
||||||
data class Enum(override val property: KProperty<kotlin.Enum<*>>) : BlockAttribute<kotlin.Enum<*>>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun generateStates(properties: List<BlockAttribute<*>>): List<Array<KPropertyValuePair<*>>> {
|
inline fun <reified T : Block> meta(id: Int, firstStateID: Int) = object : Meta<T> {
|
||||||
if (properties.isEmpty()) return emptyList()
|
override val blockClass: KClass<T> = T::class
|
||||||
|
override val codec: BlockCodec<T> = BlockCodec(blockClass, id, firstStateID)
|
||||||
val current = properties[0]
|
|
||||||
|
|
||||||
val allowedValues: Array<out Any> = when (current) {
|
|
||||||
is BlockAttribute.Boolean -> arrayOf(true, false)
|
|
||||||
is BlockAttribute.Int -> (0..current.max).toList().toTypedArray()
|
|
||||||
is BlockAttribute.Enum -> current.property.returnType.jvmErasure.java.enumConstants
|
|
||||||
}
|
|
||||||
|
|
||||||
val rest = generateStates(properties.drop(1))
|
|
||||||
return allowedValues.flatMap { value ->
|
|
||||||
if (rest.isEmpty()) listOf(arrayOf(KPropertyValuePair(current.property, value)))
|
|
||||||
else rest.map { arrayOf(KPropertyValuePair(current.property, value), *it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package space.blokk.world.block
|
||||||
|
|
||||||
|
import space.blokk.util.KPropertyValuePair
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
import kotlin.reflect.full.declaredMemberProperties
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.reflect.full.isSubtypeOf
|
||||||
|
import kotlin.reflect.full.starProjectedType
|
||||||
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
|
|
||||||
|
class BlockCodec<T : Block> constructor(
|
||||||
|
blockClass: KClass<T>,
|
||||||
|
val id: Int,
|
||||||
|
val firstStateID: Int
|
||||||
|
) {
|
||||||
|
val states: List<Array<KPropertyValuePair<*>>> =
|
||||||
|
generateStates(blockClass.declaredMemberProperties.mapNotNull { property ->
|
||||||
|
val annotation = property.findAnnotation<Attribute>() ?: return@mapNotNull null
|
||||||
|
val type = property.returnType
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
when {
|
||||||
|
type == Int::class.starProjectedType -> BlockAttribute.Int(
|
||||||
|
(property as KProperty1<Block, Int>),
|
||||||
|
annotation.max
|
||||||
|
)
|
||||||
|
type == Boolean::class.starProjectedType -> BlockAttribute.Boolean(property as KProperty1<Block, Boolean>)
|
||||||
|
type.isSubtypeOf(Enum::class.starProjectedType) -> BlockAttribute.Enum(property as KProperty1<Block, Enum<*>>)
|
||||||
|
else -> throw IllegalAttributeTargetType()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
class IllegalAttributeTargetType : Exception("The type of the target property is not allowed for attributes")
|
||||||
|
|
||||||
|
fun getStateID(block: T): Int {
|
||||||
|
val values = mutableMapOf<KProperty<*>, Any>()
|
||||||
|
|
||||||
|
val index = states.indexOfFirst { propertyValuePairs ->
|
||||||
|
propertyValuePairs.all {
|
||||||
|
values.getOrPut(it.property) { it.property.call(block)!! } == it.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
|
||||||
|
return firstStateID + index
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class BlockAttribute<T : Any> {
|
||||||
|
abstract val property: KProperty1<Block, T>
|
||||||
|
|
||||||
|
data class Boolean(override val property: KProperty1<Block, kotlin.Boolean>) : BlockAttribute<kotlin.Boolean>()
|
||||||
|
data class Int(override val property: KProperty1<Block, kotlin.Int>, val max: kotlin.Int) :
|
||||||
|
BlockAttribute<kotlin.Int>()
|
||||||
|
|
||||||
|
data class Enum(override val property: KProperty1<Block, kotlin.Enum<*>>) : BlockAttribute<kotlin.Enum<*>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun generateStates(properties: List<BlockAttribute<*>>): List<Array<KPropertyValuePair<*>>> {
|
||||||
|
if (properties.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val current = properties[0]
|
||||||
|
|
||||||
|
val allowedValues: Array<out Any> = when (current) {
|
||||||
|
is BlockAttribute.Boolean -> arrayOf(true, false)
|
||||||
|
is BlockAttribute.Int -> (0..current.max).toList().toTypedArray()
|
||||||
|
is BlockAttribute.Enum -> current.property.returnType.jvmErasure.java.enumConstants
|
||||||
|
}
|
||||||
|
|
||||||
|
val rest = generateStates(properties.drop(1))
|
||||||
|
return allowedValues.flatMap { value ->
|
||||||
|
if (rest.isEmpty()) listOf(arrayOf(KPropertyValuePair(current.property, value)))
|
||||||
|
else rest.map { arrayOf(KPropertyValuePair(current.property, value), *it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,51 +0,0 @@
|
||||||
package space.blokk.world.block
|
|
||||||
|
|
||||||
import space.blokk.world.BlockLocation
|
|
||||||
import space.blokk.world.Chunk
|
|
||||||
import space.blokk.world.World
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the chunk containing [block] is loaded.
|
|
||||||
*/
|
|
||||||
val loaded: Boolean get() = chunk.loaded
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shorthand for [`chunk.load()`][Chunk.load].
|
|
||||||
*/
|
|
||||||
suspend fun load() = chunk.load()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new [Block] of type [material] and assign it to this ref (i.e. place it in the world).
|
|
||||||
*/
|
|
||||||
abstract suspend fun place(material: Material)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun of(world: World, location: BlockLocation) = world.getBlock(location)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +1,13 @@
|
||||||
package space.blokk.world.block
|
package space.blokk.world.block
|
||||||
|
|
||||||
import space.blokk.util.clamp
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR]
|
* Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR]
|
||||||
*/
|
*/
|
||||||
class DaylightDetector(
|
data class DaylightDetector(
|
||||||
ref: BlockRef
|
@Attribute
|
||||||
) : Block(ref) {
|
val inverted: Boolean = false,
|
||||||
var inverted: Boolean = false
|
@Attribute(15)
|
||||||
var power by clamp(0, 15) { 0 }
|
val power: Int
|
||||||
|
) : Block() {
|
||||||
companion object : Block.Companion() {
|
companion object : Meta<DaylightDetector> by meta(1, 6158)
|
||||||
override val codec = Codec
|
|
||||||
.id<DaylightDetector>(325, 6158)
|
|
||||||
.attribute(DaylightDetector::inverted)
|
|
||||||
.attribute(DaylightDetector::power, 15)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package space.blokk.world.block
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Material: [HOPPER][Material.HOPPER]
|
|
||||||
*/
|
|
||||||
class Hopper(
|
|
||||||
ref: BlockRef
|
|
||||||
) : Block(ref) {
|
|
||||||
var enabled: Boolean = true
|
|
||||||
|
|
||||||
var facing: Facing = Facing.DOWN
|
|
||||||
|
|
||||||
enum class Facing {
|
|
||||||
DOWN,
|
|
||||||
NORTH,
|
|
||||||
SOUTH,
|
|
||||||
WEST,
|
|
||||||
EAST
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : Block.Companion() {
|
|
||||||
override val codec = Codec
|
|
||||||
.id<Hopper>(328, 6192)
|
|
||||||
.attribute(Hopper::enabled)
|
|
||||||
.attribute(Hopper::facing)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +1,17 @@
|
||||||
package space.blokk.world.block
|
package space.blokk.world.block
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import space.blokk.world.BlockLocation
|
|
||||||
import space.blokk.world.World
|
|
||||||
import strikt.api.expectThat
|
import strikt.api.expectThat
|
||||||
import strikt.assertions.isEqualTo
|
import strikt.assertions.isEqualTo
|
||||||
|
|
||||||
class BlockCodecTest {
|
class BlockCodecTest {
|
||||||
private val ref = object : BlockRef() {
|
|
||||||
override val location: BlockLocation get() = error("")
|
|
||||||
override val world: World get() = error("")
|
|
||||||
override var block: Block = DaylightDetector(this)
|
|
||||||
|
|
||||||
override suspend fun place(material: Material) = error("")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getStateID returns 6158 for DaylightDetector(inverted=true, power=0)`() {
|
fun `getStateID returns 6158 for DaylightDetector(inverted=true, power=0)`() {
|
||||||
val daylightDetector = ref.block as DaylightDetector
|
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(true, 0))).isEqualTo(6158)
|
||||||
daylightDetector.inverted = true
|
|
||||||
daylightDetector.power = 0
|
|
||||||
expectThat(DaylightDetector.codec.getStateID(daylightDetector)).isEqualTo(6158)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getStateID returns 6189 for DaylightDetector(inverted=false, power=15)`() {
|
fun `getStateID returns 6189 for DaylightDetector(inverted=false, power=15)`() {
|
||||||
val daylightDetector = ref.block as DaylightDetector
|
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(false, 15))).isEqualTo(6189)
|
||||||
daylightDetector.inverted = false
|
|
||||||
daylightDetector.power = 15
|
|
||||||
expectThat(DaylightDetector.codec.getStateID(daylightDetector)).isEqualTo(6189)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,34 +82,22 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File, pri
|
||||||
if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue
|
if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue
|
||||||
|
|
||||||
val type = TypeSpec.classBuilder(upperCamelName)
|
val type = TypeSpec.classBuilder(upperCamelName)
|
||||||
|
.apply {
|
||||||
|
if (block.get("states").asList().isNotEmpty()) {
|
||||||
|
addModifiers(KModifier.DATA)
|
||||||
|
primaryConstructor(
|
||||||
|
FunSpec.constructorBuilder().addParameter("PLACEHOLDER", Unit::class).build()
|
||||||
|
)
|
||||||
|
addProperty(PropertySpec.builder("PLACEHOLDER", Unit::class).initializer("PLACEHOLDER").build())
|
||||||
|
}
|
||||||
|
}
|
||||||
.addKdoc("Material: [$upperUnderscoreName][%T]", specificMaterialType)
|
.addKdoc("Material: [$upperUnderscoreName][%T]", specificMaterialType)
|
||||||
.superclass(BLOCK_TYPE)
|
.superclass(BLOCK_TYPE)
|
||||||
.primaryConstructor(
|
|
||||||
FunSpec.constructorBuilder()
|
|
||||||
.addParameter("ref", BLOCK_REF_TYPE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.addSuperclassConstructorParameter("ref")
|
|
||||||
.addType(
|
.addType(
|
||||||
TypeSpec.companionObjectBuilder()
|
TypeSpec.companionObjectBuilder()
|
||||||
.superclass(BLOCK_TYPE.nestedClass("Companion"))
|
.addSuperinterface(
|
||||||
.addProperty(
|
BLOCK_TYPE.nestedClass("Meta").parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)),
|
||||||
PropertySpec.builder(
|
CodeBlock.of("meta(${block.get("id").toInt()}, ${block.get("minStateId").toInt()})")
|
||||||
"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()
|
||||||
)
|
)
|
||||||
|
@ -120,9 +108,7 @@ class FilesGenerator(private val dataDir: File, private val outputDir: File, pri
|
||||||
.addType(type)
|
.addType(type)
|
||||||
.build()
|
.build()
|
||||||
.toString()
|
.toString()
|
||||||
.replaceFirst("\n\nimport $IMPORT_FOR_REMOVAL_PACKAGE_NAME.$PROPERTY_TYPE_FOR_REMOVAL_NAME", "")
|
.replaceFirst("Block.Meta", "Meta")
|
||||||
.replaceFirst(": $PROPERTY_TYPE_FOR_REMOVAL_NAME", "")
|
|
||||||
.replaceFirst("Block.Codec", "Codec")
|
|
||||||
|
|
||||||
outputDir.resolve(filePathRelativeToSourceRoot).writeText(fileContent)
|
outputDir.resolve(filePathRelativeToSourceRoot).writeText(fileContent)
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue