Archived
1
0
Fork 0

Add entity and fluid generators

This commit is contained in:
Moritz Ruth 2020-12-17 20:57:58 +01:00
parent 647cf1b4a0
commit 393e9d35c8
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
16 changed files with 205 additions and 42 deletions

View file

@ -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
}
}
}

View 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)
}
}

View file

@ -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.

View file

@ -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()
}
}

View file

@ -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`() {

View file

@ -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)

View file

@ -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

View file

@ -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) {

View file

@ -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"
)
}

View file

@ -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()

View file

@ -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()
}
}
}

View file

@ -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()

View file

@ -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)
}
}

View file

@ -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")

View file

@ -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)
}
}

View file

@ -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)
}
}