Add entity and fluid generators
This commit is contained in:
parent
647cf1b4a0
commit
393e9d35c8
16 changed files with 205 additions and 42 deletions
|
@ -1,8 +1,30 @@
|
|||
package space.blokk.entity
|
||||
|
||||
import space.blokk.NamespacedID
|
||||
import space.blokk.event.Event
|
||||
import space.blokk.event.EventBus
|
||||
import space.blokk.event.EventTarget
|
||||
import space.blokk.logging.Logger
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface Entity : EventTarget<Event> {
|
||||
// TODO
|
||||
abstract class Entity internal constructor() : EventTarget<Event> {
|
||||
val uuid: UUID = UUID.randomUUID()
|
||||
|
||||
override val eventBus: EventBus<Event> = EventBus(Event::class, Logger("Entity/$uuid"))
|
||||
|
||||
companion object {
|
||||
internal inline fun <reified T : Entity> type(
|
||||
numericID: Int,
|
||||
id: NamespacedID,
|
||||
width: Float,
|
||||
height: Float
|
||||
) : EntityType<T> = object : EntityType<T> {
|
||||
override val entityClass: KClass<T> = T::class
|
||||
override val numericID: Int = numericID
|
||||
override val id: NamespacedID = id
|
||||
override val width: Float = width
|
||||
override val height: Float = height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
blokk-api/src/main/kotlin/space/blokk/entity/EntityType.kt
Normal file
28
blokk-api/src/main/kotlin/space/blokk/entity/EntityType.kt
Normal file
|
@ -0,0 +1,28 @@
|
|||
package space.blokk.entity
|
||||
|
||||
import space.blokk.NamespacedID
|
||||
import space.blokk.world.block.Block
|
||||
import space.blokk.world.block.Material
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface EntityType<T : Entity> {
|
||||
val entityClass: KClass<T>
|
||||
val numericID: Int
|
||||
val id: NamespacedID
|
||||
val width: Float
|
||||
val height: Float
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* All entity types, sorted by their numeric ID in ascending order.
|
||||
*/
|
||||
val all = GENERATED_ENTITIES
|
||||
val byID = GENERATED_ENTITIES.map { it.id to it }.toMap()
|
||||
|
||||
private val byClass = GENERATED_ENTITIES.map { it.entityClass to it }.toMap()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T: Entity> byClass(entityClass: KClass<T>): EntityType<T>? = byClass[entityClass] as EntityType<T>?
|
||||
inline fun <reified T: Entity> byClass(): EntityType<T>? = byClass(T::class)
|
||||
}
|
||||
}
|
|
@ -13,8 +13,8 @@ import kotlin.system.measureTimeMillis
|
|||
|
||||
class EventBus<EventT : Event>(
|
||||
private val eventClass: KClass<EventT>,
|
||||
private val scope: CoroutineScope,
|
||||
private val logger: Logger
|
||||
private val logger: Logger,
|
||||
private val scope: CoroutineScope = Blokk.server.scope
|
||||
) {
|
||||
/**
|
||||
* All event handlers, sorted by their priority and the order in which they were registered.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package space.blokk.tags
|
||||
|
||||
import space.blokk.NamespacedID
|
||||
import space.blokk.entity.EntityType
|
||||
import space.blokk.world.NUMERIC_FLUID_IDS_BY_ID
|
||||
import space.blokk.world.block.Material
|
||||
|
||||
// TODO: Replace lazy properties with functions called during startup
|
||||
|
@ -15,7 +17,9 @@ class Tag(val name: String, val type: Type, val rawValues: List<String>) {
|
|||
val numericIDs: List<Int> by lazy {
|
||||
when (type) {
|
||||
Type.BLOCKS -> values.map { Material.byID.getValue(it).codec.id }
|
||||
else -> emptyList() // TODO
|
||||
Type.ENTITY_TYPES -> values.map { EntityType.byID.getValue(it).numericID }
|
||||
Type.FLUIDS -> values.map { NUMERIC_FLUID_IDS_BY_ID.getValue(it) }
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class EventBusTest {
|
|||
mockBlokkProvider()
|
||||
}
|
||||
|
||||
private val eventBus = EventBus(TestEvent::class, CoroutineScope(Dispatchers.Default), Logger("logger"))
|
||||
private val eventBus = EventBus(TestEvent::class, Logger("logger"), CoroutineScope(Dispatchers.Default))
|
||||
|
||||
@Test
|
||||
fun `calls the handler exactly 1 time when the event is emitted 1 time`() {
|
||||
|
|
|
@ -28,7 +28,7 @@ class BlokkServer internal constructor() : Server {
|
|||
private val socketServer = BlokkSocketServer(this)
|
||||
|
||||
override val scope = createUnconfinedSupervisorScope("Server")
|
||||
override val eventBus = EventBus(ServerEvent::class, scope, logger)
|
||||
override val eventBus = EventBus(ServerEvent::class, logger, scope)
|
||||
override val sessions by socketServer::sessions
|
||||
override val players = EventTargetGroup.Mutable<Player>(true)
|
||||
override val pluginManager = BlokkPluginManager(this)
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf
|
|||
import io.netty.channel.Channel
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import space.blokk.BlokkServer
|
||||
import space.blokk.chat.ChatColor
|
||||
import space.blokk.chat.ChatComponent
|
||||
|
@ -55,7 +54,7 @@ class BlokkSession(private val channel: Channel, val server: BlokkServer) : Sess
|
|||
}
|
||||
|
||||
override val scope = createUnconfinedSupervisorScope(identifier)
|
||||
override val eventBus = EventBus(SessionEvent::class, scope, logger)
|
||||
override val eventBus = EventBus(SessionEvent::class, logger, scope)
|
||||
override var brand: String? = null; internal set
|
||||
|
||||
private var disconnectReason: String? = null
|
||||
|
|
|
@ -31,7 +31,7 @@ class BlokkPlayer(
|
|||
|
||||
override val scope by lazy { createUnconfinedSupervisorScope(identifier, session.scope.coroutineContext[Job]) }
|
||||
|
||||
override val eventBus = EventBus(PlayerEvent::class, scope, logger)
|
||||
override val eventBus = EventBus(PlayerEvent::class, logger, scope)
|
||||
|
||||
override var selectedHotbarSlot: Byte = 0
|
||||
set(value) {
|
||||
|
|
|
@ -13,7 +13,7 @@ class DataDownloader(private val dir: File) {
|
|||
val file = dir.resolve(name)
|
||||
file.parentFile.mkdirs()
|
||||
|
||||
if (file.exists()) println("name already exists, skipping download")
|
||||
if (file.exists()) println("$name already exists, skipping download")
|
||||
else {
|
||||
val request = Request.Builder()
|
||||
.get()
|
||||
|
@ -41,6 +41,7 @@ class DataDownloader(private val dir: File) {
|
|||
"minecraft_server.jar" to "https://launcher.mojang.com/v1/objects/4d1826eebac84847c71a77f9349cc22afd0cf0a1/server.jar",
|
||||
"blocks.json" to PRISMARINE_BASE_URL + "1.15.2/blocks.json",
|
||||
"biomes.json" to PRISMARINE_BASE_URL + "1.15.2/biomes.json",
|
||||
"entities.json" to PRISMARINE_BASE_URL + "1.15.2/entities.json",
|
||||
"blockCollisionShapes.json" to PRISMARINE_BASE_URL + "1.15.2/blockCollisionShapes.json"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ class DataGeneratorRunner(private val workingDir: File, private val outputDir: F
|
|||
"-cp",
|
||||
minecraftServerFile.absolutePath,
|
||||
"net.minecraft.data.Main",
|
||||
"--server"
|
||||
"--server",
|
||||
"--reports"
|
||||
)
|
||||
.directory(workingDir)
|
||||
.start()
|
||||
|
|
|
@ -2,9 +2,7 @@ package space.blokk.mdsp
|
|||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import space.blokk.mdsp.generator.BiomesGenerator
|
||||
import space.blokk.mdsp.generator.BlocksAndMaterialGenerator
|
||||
import space.blokk.mdsp.generator.TagsFileGenerator
|
||||
import space.blokk.mdsp.generator.*
|
||||
|
||||
class MinecraftDataSourcesPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
|
@ -42,9 +40,14 @@ class MinecraftDataSourcesPlugin : Plugin<Project> {
|
|||
it.dependsOn(runDataGeneratorsTask)
|
||||
|
||||
it.doLast {
|
||||
outputDir.deleteRecursively()
|
||||
outputDir.mkdirs()
|
||||
|
||||
BlocksAndMaterialGenerator(workingDir, outputDir, sourcesDir).generate()
|
||||
TagsFileGenerator(workingDir, outputDir).generate()
|
||||
BiomesGenerator(workingDir, outputDir).generate()
|
||||
EntitiesGenerator(workingDir, outputDir, sourcesDir).generate()
|
||||
FluidIDMapGenerator(workingDir, outputDir).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,7 @@ import com.squareup.kotlinpoet.TypeSpec
|
|||
import space.blokk.mdsp.util.ConstructorPropertiesHelper
|
||||
import java.io.File
|
||||
|
||||
class BiomesGenerator(val workingDir: File, val outputDir: File) {
|
||||
companion object {
|
||||
val NAMESPACED_ID_TYPE = ClassName("space.blokk", "NamespacedID")
|
||||
}
|
||||
|
||||
class BiomesGenerator(private val workingDir: File, private val outputDir: File) {
|
||||
fun generate() {
|
||||
val cph = ConstructorPropertiesHelper()
|
||||
|
||||
|
@ -40,7 +36,7 @@ class BiomesGenerator(val workingDir: File, val outputDir: File) {
|
|||
.addSuperclassConstructorParameter("%L", numericID)
|
||||
.addSuperclassConstructorParameter(
|
||||
"%T(%S)",
|
||||
BlocksAndMaterialGenerator.NAMESPACED_ID_TYPE,
|
||||
NAMESPACED_ID_TYPE,
|
||||
"minecraft:$id"
|
||||
)
|
||||
.build()
|
||||
|
|
|
@ -2,11 +2,9 @@ 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
|
||||
import space.blokk.mdsp.util.ConstructorPropertiesHelper
|
||||
import java.io.File
|
||||
|
||||
class BlocksAndMaterialGenerator(
|
||||
|
@ -15,11 +13,6 @@ class BlocksAndMaterialGenerator(
|
|||
private val sourcesDir: File
|
||||
) {
|
||||
companion object {
|
||||
const val BLOCK_PACKAGE = "space.blokk.world.block"
|
||||
val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material")
|
||||
val NAMESPACED_ID_TYPE = ClassName("space.blokk", "NamespacedID")
|
||||
val BLOCK_TYPE = ClassName(BLOCK_PACKAGE, "Block")
|
||||
|
||||
val BLOCK_BLACKLIST = listOf(
|
||||
// Only used on the client side for y values lower than 0 and higher than 255
|
||||
"void_air"
|
||||
|
@ -27,9 +20,6 @@ class BlocksAndMaterialGenerator(
|
|||
}
|
||||
|
||||
fun generate() {
|
||||
outputDir.deleteRecursively()
|
||||
outputDir.mkdirs()
|
||||
|
||||
val collisionShapesJson = workingDir.resolve("blockCollisionShapes.json").readText()
|
||||
val collisionShapesData = JsonIterator.deserialize(collisionShapesJson)
|
||||
generateCollisionShapes(collisionShapesData.get("shapes").asMap())
|
||||
|
@ -73,6 +63,8 @@ class BlocksAndMaterialGenerator(
|
|||
.writeTo(outputDir)
|
||||
}
|
||||
|
||||
val materialLines = listOf("%L", "%T(%S)", "%L", "%Lf", "%L", "%L", "%L", "BLOCK_COLLISION_SHAPES[%L]")
|
||||
|
||||
private fun generateBlockStubs(blocks: List<JsonAny>, collisionShapes: Map<String, JsonAny>) {
|
||||
for (block in blocks) {
|
||||
val lowerUnderscoreName = block.get("name").toString()
|
||||
|
@ -83,9 +75,7 @@ class BlocksAndMaterialGenerator(
|
|||
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(
|
||||
val materialArgs = arrayOf(
|
||||
block.get("id").toInt(),
|
||||
NAMESPACED_ID_TYPE,
|
||||
"minecraft:$lowerUnderscoreName",
|
||||
|
@ -102,7 +92,9 @@ class BlocksAndMaterialGenerator(
|
|||
if (block.get("states").asList().isNotEmpty()) {
|
||||
addModifiers(KModifier.DATA)
|
||||
primaryConstructor(
|
||||
FunSpec.constructorBuilder().addParameter("PLACEHOLDER", Unit::class).build()
|
||||
FunSpec.constructorBuilder().addParameter(
|
||||
ParameterSpec.builder("PLACEHOLDER", Unit::class).defaultValue("Unit").build()
|
||||
).build()
|
||||
)
|
||||
addProperty(PropertySpec.builder("PLACEHOLDER", Unit::class).initializer("PLACEHOLDER").build())
|
||||
}
|
||||
|
@ -112,20 +104,16 @@ class BlocksAndMaterialGenerator(
|
|||
TypeSpec.companionObjectBuilder()
|
||||
.addSuperinterface(
|
||||
MATERIAL_TYPE.parameterizedBy(ClassName(BLOCK_PACKAGE, upperCamelName)),
|
||||
CodeBlock.of("material(\n${metaLines.joinToString(",\n") { " $it" }}\n)", *metaArgs)
|
||||
CodeBlock.of("material(\n${materialLines.joinToString(",\n") { " $it" }}\n)", *materialArgs)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
val fileContent = FileSpec.builder(BLOCK_PACKAGE, upperCamelName)
|
||||
.addComment("IF YOU CHANGE THIS FILE, MOVE IT FROM `generatedKotlin` TO `kotlin` OR IT WILL BE OVERWRITTEN.")
|
||||
FileSpec.builder(BLOCK_PACKAGE, upperCamelName)
|
||||
.addType(type)
|
||||
.build()
|
||||
.toString()
|
||||
.replaceFirst("Block.Meta", "Meta")
|
||||
|
||||
outputDir.resolve(filePathRelativeToSourceRoot).writeText(fileContent)
|
||||
.writeTo(outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package space.blokk.mdsp.generator
|
||||
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
|
||||
const val BASE_PACKAGE = "space.blokk"
|
||||
const val WORLD_PACKAGE = "$BASE_PACKAGE.world"
|
||||
const val BLOCK_PACKAGE = "$WORLD_PACKAGE.block"
|
||||
const val ENTITY_PACKAGE = "$BASE_PACKAGE.entity"
|
||||
val MATERIAL_TYPE = ClassName(BLOCK_PACKAGE, "Material")
|
||||
val NAMESPACED_ID_TYPE = ClassName(BASE_PACKAGE, "NamespacedID")
|
||||
val BLOCK_TYPE = ClassName(BLOCK_PACKAGE, "Block")
|
||||
val ENTITY_TYPE = ClassName(ENTITY_PACKAGE, "Entity")
|
||||
val ENTITY_TYPE_TYPE = ClassName(ENTITY_PACKAGE, "EntityType")
|
|
@ -0,0 +1,76 @@
|
|||
package space.blokk.mdsp.generator
|
||||
|
||||
import com.google.common.base.CaseFormat
|
||||
import com.jsoniter.JsonIterator
|
||||
import com.squareup.kotlinpoet.*
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import space.blokk.mdsp.JsonAny
|
||||
import java.io.File
|
||||
|
||||
class EntitiesGenerator(
|
||||
private val workingDir: File,
|
||||
private val outputDir: File,
|
||||
private val sourcesDir: File
|
||||
) {
|
||||
fun generate() {
|
||||
val entitiesJson = workingDir.resolve("entities.json").readText()
|
||||
val entities = JsonIterator.deserialize(entitiesJson).asList()
|
||||
|
||||
generateEntityStubs(entities)
|
||||
generateEntitiesList(entities)
|
||||
}
|
||||
|
||||
private val typeLines = listOf("%L", "%T(%S)", "%Lf", "%Lf")
|
||||
|
||||
private fun generateEntityStubs(entities: List<JsonAny>) {
|
||||
for (entity in entities) {
|
||||
val name = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, entity.get("name").toString())!!
|
||||
|
||||
val filePathRelativeToSourceRoot = "./${BLOCK_PACKAGE.replace(".", "/")}/$name.kt"
|
||||
if (sourcesDir.resolve(filePathRelativeToSourceRoot).exists()) continue
|
||||
|
||||
val typeArgs = arrayOf(
|
||||
entity.get("id").toInt(),
|
||||
NAMESPACED_ID_TYPE,
|
||||
"minecraft:" + entity.get("name").toString(),
|
||||
entity.get("width").toFloat(),
|
||||
entity.get("height").toFloat()
|
||||
)
|
||||
|
||||
val type = TypeSpec.classBuilder(name)
|
||||
.superclass(ENTITY_TYPE)
|
||||
.addType(
|
||||
TypeSpec.companionObjectBuilder()
|
||||
.addSuperinterface(
|
||||
ENTITY_TYPE_TYPE.parameterizedBy(ClassName(ENTITY_PACKAGE, name)),
|
||||
CodeBlock.of("type(\n${typeLines.joinToString(",\n") { " $it" }}\n)", *typeArgs)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
FileSpec.builder(ENTITY_PACKAGE, name)
|
||||
.addType(type)
|
||||
.build()
|
||||
.writeTo(outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateEntitiesList(entities: List<JsonAny>) {
|
||||
val names = entities
|
||||
.map { it.get("name").toString() }
|
||||
.map { CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, it) }
|
||||
|
||||
val property = PropertySpec.builder(
|
||||
"GENERATED_ENTITIES",
|
||||
List::class.asTypeName().parameterizedBy(ENTITY_TYPE_TYPE.parameterizedBy(STAR))
|
||||
)
|
||||
.initializer("listOf(\n${names.joinToString(",\n")}\n)")
|
||||
.build()
|
||||
|
||||
FileSpec.builder(ENTITY_PACKAGE, "Entities")
|
||||
.addProperty(property)
|
||||
.build()
|
||||
.writeTo(outputDir)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package space.blokk.mdsp.generator
|
||||
|
||||
import com.google.common.base.CaseFormat
|
||||
import com.jsoniter.JsonIterator
|
||||
import com.squareup.kotlinpoet.*
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||
import java.io.File
|
||||
|
||||
class FluidIDMapGenerator(
|
||||
private val workingDir: File,
|
||||
private val outputDir: File
|
||||
) {
|
||||
fun generate() {
|
||||
val json = workingDir.resolve("generated/reports/registries.json").readText()
|
||||
val registries = JsonIterator.deserialize(json)
|
||||
|
||||
generateFluidIDMap(registries.get("minecraft:fluid").get("entries").asMap().map { it.key to it.value.get("protocol_id").toInt() }.toMap())
|
||||
}
|
||||
|
||||
private fun generateFluidIDMap(fluids: Map<String, Int>) {
|
||||
val args = fluids.flatMap { listOf(NAMESPACED_ID_TYPE, it.key, it.value) }.toTypedArray()
|
||||
|
||||
val property = PropertySpec.builder("NUMERIC_FLUID_IDS_BY_ID", Map::class.asTypeName().parameterizedBy(NAMESPACED_ID_TYPE, Int::class.asTypeName()))
|
||||
.initializer("mapOf(\n${fluids.values.indices.joinToString(",\n") { "%T(%S) to %L" }}\n)", *args)
|
||||
.build()
|
||||
|
||||
FileSpec.builder(WORLD_PACKAGE, "NumericFluidIDsByID")
|
||||
.addProperty(property)
|
||||
.build()
|
||||
.writeTo(outputDir)
|
||||
}
|
||||
}
|
Reference in a new issue