make the data being output by the events-mod more generalised, to include more than just events (mod config, prototypes)
This commit is contained in:
parent
58233c9343
commit
ed4d42a2ef
26 changed files with 524 additions and 379 deletions
|
|
@ -80,8 +80,8 @@ val generateTypescript by tasks.registering(JavaExec::class) {
|
|||
|
||||
mainClass.set("dev.adamko.kafkatorio.events.schema.Kt2tsKt")
|
||||
|
||||
val generatedFile = file("$temporaryDir/${project.name}.ts")
|
||||
args(generatedFile)
|
||||
val tempOutputDirectory = file("$temporaryDir")
|
||||
args(tempOutputDirectory)
|
||||
|
||||
val buildOutput = layout.buildDirectory.dir("generated/typescript")
|
||||
outputs.dir(buildOutput)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package dev.adamko.kafkatorio.events.schema
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PositionData(
|
||||
val x: Double,
|
||||
val y: Double,
|
||||
val type: PositionType?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
enum class PositionType {
|
||||
MAP, CHUNK, TILE,
|
||||
}
|
||||
|
||||
/**
|
||||
* Red, green, blue and alpha values, all in range `[0, 1]` or all in range `[0, 255]` if any
|
||||
* value is > 1.
|
||||
*
|
||||
* All values here are optional. Color channels default to 0, the alpha channel defaults to 1.
|
||||
*/
|
||||
@Serializable
|
||||
data class Colour(
|
||||
val red: Float = 0f,
|
||||
val green: Float = 0f,
|
||||
val blue: Float = 0f,
|
||||
val alpha: Float = 1f,
|
||||
) {
|
||||
/** True if any value is greater than 1, so the values are hexadecimal. */
|
||||
fun isDecimal(): Boolean {
|
||||
return red > 1f
|
||||
|| green > 1f
|
||||
|| blue > 1f
|
||||
|| alpha > 1f
|
||||
}
|
||||
|
||||
/** True if all values are between `[0..1]`. */
|
||||
fun isPercentage(): Boolean = !isDecimal()
|
||||
|
||||
}
|
||||
|
||||
//@Serializable
|
||||
//@SerialName("FactorioTiles")
|
||||
//data class FactorioTiles(
|
||||
// val tiles: List<FactorioTile>,
|
||||
//) : FactorioObjectData() {
|
||||
// @Transient
|
||||
// override val objectName: String = "FactorioTiles"
|
||||
//}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package dev.adamko.kafkatorio.events.schema
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
@Serializable
|
||||
data class FactorioConfigurationUpdate(
|
||||
override val modVersion: String,
|
||||
val factorioData: FactorioGameDataUpdate,
|
||||
val allMods: List<FactorioModInfo>,
|
||||
val prototypes: List<FactorioPrototype>,
|
||||
) : KafkatorioPacket
|
||||
|
||||
@Serializable
|
||||
data class FactorioGameDataUpdate(
|
||||
val oldVersion: String?,
|
||||
val newVersion: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FactorioModInfo(
|
||||
val modName: String,
|
||||
val currentVersion: String?,
|
||||
val previousVersion: String?,
|
||||
)
|
||||
|
|
@ -1,192 +1,16 @@
|
|||
package dev.adamko.kafkatorio.events.schema
|
||||
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||
|
||||
|
||||
//@Serializable(with = FactorioEventSerializer::class)
|
||||
@Serializable
|
||||
data class FactorioEvent<out T : FactorioObjectData>(
|
||||
override val modVersion: String,
|
||||
|
||||
/** the initial Factorio event ({defines.events}) trigger */
|
||||
@SerialName("event_type")
|
||||
val eventType: String,
|
||||
/** Schema versioning */
|
||||
@SerialName("mod_version")
|
||||
val modVersion: String,
|
||||
@SerialName("factorio_version")
|
||||
val factorioVersion: String,
|
||||
/** game time */
|
||||
@SerialName("tick")
|
||||
val tick: UInt,
|
||||
@SerialName("data")
|
||||
val data: T
|
||||
)
|
||||
|
||||
|
||||
/** The [JsonClassDiscriminator] for [FactorioEvent] */
|
||||
const val FactorioObjectDataDiscriminatorKey: String = "object_name"
|
||||
|
||||
|
||||
@Serializable
|
||||
@JsonClassDiscriminator(FactorioObjectDataDiscriminatorKey)
|
||||
sealed class FactorioObjectData {
|
||||
|
||||
abstract val objectName: String
|
||||
|
||||
// workaround - https://github.com/Kotlin/kotlinx.serialization/issues/1664
|
||||
// val objectName: String by lazy {
|
||||
// serializer().descriptor.serialName
|
||||
// }
|
||||
|
||||
// val objectName: String by lazy {
|
||||
// kotlinx.serialization.serializer(this::class.starProjectedType).descriptor.serialName
|
||||
// }
|
||||
|
||||
// alternative: find the @SerialName annotation
|
||||
// Again it must be delegated so there's no backing field and kxs ignores it
|
||||
// val objectName: String by lazy {
|
||||
// requireNotNull(this::class.findAnnotation<SerialName>()?.value) {
|
||||
// "Couldn't find @SerialName for ${this::class}!"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaPlayer")
|
||||
data class PlayerData(
|
||||
@Serializable(with = ListAsObjectSerializer::class)
|
||||
@SerialName("associated_characters_unit_numbers")
|
||||
val associatedCharactersUnitNumbers: List<UInt>,
|
||||
@SerialName("character_unit_number")
|
||||
val characterUnitNumber: UInt?,
|
||||
@SerialName("name")
|
||||
val name: String,
|
||||
@SerialName("position")
|
||||
val position: PositionData,
|
||||
@SerialName("colour")
|
||||
val colour: Colour,
|
||||
@SerialName("chat_colour")
|
||||
val chatColour: Colour,
|
||||
@SerialName("last_online")
|
||||
val lastOnline: UInt,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaPlayer"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaEntity")
|
||||
data class EntityData(
|
||||
@SerialName("active")
|
||||
val active: Boolean,
|
||||
@SerialName("health")
|
||||
val health: Double?,
|
||||
@SerialName("health_ratio")
|
||||
val healthRatio: Double,
|
||||
@SerialName("name")
|
||||
val name: String,
|
||||
@SerialName("position")
|
||||
val position: PositionData,
|
||||
@SerialName("surface_index")
|
||||
val surfaceIndex: Int,
|
||||
@SerialName("type")
|
||||
val type: String,
|
||||
@SerialName("unit_number")
|
||||
val unitNumber: UInt? = null,
|
||||
|
||||
@SerialName("player_index")
|
||||
val playerIndex: UInt? = null,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaEntity"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaSurface")
|
||||
data class SurfaceData(
|
||||
@SerialName("daytime")
|
||||
val daytime: Double,
|
||||
@SerialName("index")
|
||||
val index: UInt,
|
||||
@SerialName("name")
|
||||
val name: String,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaSurface"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PositionData(
|
||||
val x: Double,
|
||||
val y: Double,
|
||||
)
|
||||
|
||||
/**
|
||||
* Red, green, blue and alpha values, all in range `[0, 1]` or all in range `[0, 255]` if any
|
||||
* value is > 1.
|
||||
*
|
||||
* All values here are optional. Color channels default to 0, the alpha channel defaults to 1.
|
||||
*/
|
||||
@Serializable
|
||||
data class Colour(
|
||||
val red: Float = 0f,
|
||||
val green: Float = 0f,
|
||||
val blue: Float = 0f,
|
||||
val alpha: Float = 1f,
|
||||
) {
|
||||
/** True if any value is greater than 1, so the values are hexadecimal. */
|
||||
fun isDecimal(): Boolean {
|
||||
return red > 1f
|
||||
|| green > 1f
|
||||
|| blue > 1f
|
||||
|| alpha > 1f
|
||||
}
|
||||
|
||||
/** True if all values are between `[0..1]`. */
|
||||
fun isPercentage(): Boolean = !isDecimal()
|
||||
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ConsoleChatMessage(
|
||||
@SerialName("author_player_index")
|
||||
val authorPlayerIndex: UInt?,
|
||||
val content: String,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "ConsoleChatMessage"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("FactorioTilesMap")
|
||||
data class FactorioTilesMap(
|
||||
val tiles: Map<PositionData, FactorioTile>,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "FactorioTilesMap"
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
@SerialName("FactorioTiles")
|
||||
data class FactorioTiles(
|
||||
val tiles: List<FactorioTile>,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "FactorioTiles"
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaTile")
|
||||
data class FactorioTile(
|
||||
val prototypeName: String,
|
||||
val position: PositionData,
|
||||
val surfaceIndex: Int,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaTile"
|
||||
}
|
||||
val data: T,
|
||||
) : KafkatorioPacket
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
package dev.adamko.kafkatorio.events.schema
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||
|
||||
@Serializable
|
||||
@JsonClassDiscriminator(FactorioObjectData.discriminatorKey)
|
||||
sealed class FactorioObjectData {
|
||||
|
||||
abstract val objectName: String
|
||||
|
||||
// workaround - https://github.com/Kotlin/kotlinx.serialization/issues/1664
|
||||
// val objectName: String by lazy {
|
||||
// serializer().descriptor.serialName
|
||||
// }
|
||||
|
||||
// val objectName: String by lazy {
|
||||
// kotlinx.serialization.serializer(this::class.starProjectedType).descriptor.serialName
|
||||
// }
|
||||
|
||||
// alternative: find the @SerialName annotation
|
||||
// Again it must be delegated so there's no backing field and kxs ignores it
|
||||
// val objectName: String by lazy {
|
||||
// requireNotNull(this::class.findAnnotation<SerialName>()?.value) {
|
||||
// "Couldn't find @SerialName for ${this::class}!"
|
||||
// }
|
||||
// }
|
||||
|
||||
companion object {
|
||||
/** The [JsonClassDiscriminator] for [FactorioEvent] */
|
||||
const val discriminatorKey: String = "objectName"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaPlayer")
|
||||
data class PlayerData(
|
||||
@Serializable(with = ListAsObjectSerializer::class)
|
||||
val associatedCharactersUnitNumbers: List<UInt>,
|
||||
val characterUnitNumber: UInt?,
|
||||
val name: String,
|
||||
val position: PositionData,
|
||||
val colour: Colour,
|
||||
val chatColour: Colour,
|
||||
val lastOnline: UInt,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaPlayer"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaEntity")
|
||||
data class EntityData(
|
||||
val active: Boolean,
|
||||
val health: Double?,
|
||||
val healthRatio: Double,
|
||||
val name: String,
|
||||
val position: PositionData,
|
||||
val surfaceIndex: Int,
|
||||
val type: String,
|
||||
val unitNumber: UInt? = null,
|
||||
val playerIndex: UInt? = null,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaEntity"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaSurface")
|
||||
data class SurfaceData(
|
||||
val daytime: Double,
|
||||
val index: UInt,
|
||||
val name: String,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaSurface"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("FactorioTilesMap")
|
||||
data class FactorioTilesMap(
|
||||
val tiles: Map<PositionData, FactorioTile>,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "FactorioTilesMap"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@SerialName("LuaTile")
|
||||
data class FactorioTile(
|
||||
val prototypeName: String,
|
||||
val position: PositionData,
|
||||
val surfaceIndex: Int,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "LuaTile"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ConsoleChatMessage(
|
||||
val authorPlayerIndex: UInt?,
|
||||
val content: String,
|
||||
) : FactorioObjectData() {
|
||||
@Transient
|
||||
override val objectName: String = "ConsoleChatMessage"
|
||||
}
|
||||
|
|
@ -6,9 +6,19 @@ import kotlinx.serialization.Transient
|
|||
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||
|
||||
@Serializable
|
||||
@JsonClassDiscriminator(FactorioObjectDataDiscriminatorKey)
|
||||
class FactorioPrototypes<T : FactorioPrototype>(
|
||||
override val modVersion: String,
|
||||
val prototypes: List<T>,
|
||||
) : KafkatorioPacket
|
||||
|
||||
@Serializable
|
||||
@JsonClassDiscriminator(FactorioPrototype.discriminatorKey)
|
||||
sealed class FactorioPrototype {
|
||||
abstract val objectName: String
|
||||
|
||||
companion object {
|
||||
const val discriminatorKey: String = "objectName"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
@ -20,7 +30,7 @@ data class FactorioMapTilePrototype(
|
|||
val collisionMasks: List<String>,
|
||||
val order: String,
|
||||
|
||||
/** Is this tile mineable at all? */
|
||||
/** Can the tile be mined for resources? */
|
||||
val canBeMined: Boolean,
|
||||
) : FactorioPrototype() {
|
||||
@Transient
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package dev.adamko.kafkatorio.events.schema;
|
||||
|
||||
|
||||
//@Serializable
|
||||
sealed interface KafkatorioPacket {
|
||||
/** Schema versioning */
|
||||
val modVersion: String
|
||||
}
|
||||
|
||||
|
||||
//enum class PacketDataType {
|
||||
// EVENT, PROTOTYPE, CONFIG_DATA,
|
||||
// ;
|
||||
//}
|
||||
|
||||
|
||||
//@Serializable
|
||||
//@JsonClassDiscriminator(KafkatorioPacketData.discriminatorKey)
|
||||
//sealed class KafkatorioPacketData(
|
||||
// @SerialName(discriminatorKey)
|
||||
// val dataType: PacketDataType
|
||||
//) {
|
||||
// companion object {
|
||||
// const val discriminatorKey: String = "dataType"
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
package dev.adamko.kafkatorio.events.schema
|
||||
|
||||
import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import me.ntrrgc.tsGenerator.ClassTransformer
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import me.ntrrgc.tsGenerator.TypeScriptGenerator
|
||||
import me.ntrrgc.tsGenerator.camelCaseToSnakeCase
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
/**
|
||||
|
|
@ -15,9 +12,8 @@ fun main(args: Array<String>) {
|
|||
|
||||
val gen =
|
||||
TypeScriptGenerator(
|
||||
rootClasses =
|
||||
buildSet {
|
||||
add(FactorioEvent::class)
|
||||
rootClasses = buildSet {
|
||||
addAll(KafkatorioPacket::class.sealedSubclasses)
|
||||
addAll(FactorioObjectData::class.sealedSubclasses)
|
||||
addAll(FactorioPrototype::class.sealedSubclasses)
|
||||
},
|
||||
|
|
@ -33,7 +29,7 @@ fun main(args: Array<String>) {
|
|||
ULong::class to "uint64",
|
||||
),
|
||||
classTransformers = listOf(
|
||||
PropertyNameTransformer,
|
||||
// PropertyNameTransformer,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -44,29 +40,60 @@ fun main(args: Array<String>) {
|
|||
println(definitions)
|
||||
}
|
||||
else -> {
|
||||
val out = File(filename)
|
||||
out.parentFile.mkdirs()
|
||||
out.printWriter().use {
|
||||
it.print(header)
|
||||
it.print(definitions)
|
||||
}
|
||||
println("Generated file: $out")
|
||||
|
||||
val outPath = Path(filename)
|
||||
outPath.createDirectories()
|
||||
|
||||
splitDefinitions(definitions)
|
||||
.forEach { (defFile, def) ->
|
||||
val outFile = outPath.resolve(defFile).toFile()
|
||||
outFile.printWriter().use {
|
||||
it.print(header)
|
||||
it.print(def)
|
||||
}
|
||||
println("Generated file: $outFile")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Split the TypeScript interfaces into different files */
|
||||
private fun splitDefinitions(definitions: String): Map<String, String> {
|
||||
return definitions
|
||||
.split("\n\n")
|
||||
.groupBy { def ->
|
||||
when {
|
||||
"extends FactorioObjectData" in def ||
|
||||
"interface FactorioObjectData" in def -> "object-data.d.ts"
|
||||
"extends FactorioPrototype" in def ||
|
||||
"interface FactorioPrototype" in def -> "prototype.d.ts"
|
||||
"extends FactorioConfigurationUpdate" in def ||
|
||||
"interface FactorioConfigurationUpdate" in def ||
|
||||
"FactorioGameDataUpdate" in def ||
|
||||
"FactorioModInfo" in def -> "config-update.d.ts"
|
||||
else -> "schema.d.ts"
|
||||
}
|
||||
}
|
||||
.mapValues { (_, def) ->
|
||||
def
|
||||
.joinToString("\n\n", postfix = "\n")
|
||||
.replace(" ", " ")
|
||||
}
|
||||
}
|
||||
|
||||
@Language("TypeScript")
|
||||
val header = """
|
||||
|// Generated by TypeScriptGenerator - do not edit this file manually
|
||||
|
|
||||
""".trimMargin()
|
||||
""".trimMargin() + "\n"
|
||||
|
||||
object PropertyNameTransformer : ClassTransformer {
|
||||
override fun transformPropertyName(
|
||||
propertyName: String,
|
||||
property: KProperty<*>,
|
||||
klass: KClass<*>
|
||||
): String {
|
||||
return camelCaseToSnakeCase(propertyName)
|
||||
}
|
||||
}
|
||||
//object PropertyNameTransformer : ClassTransformer {
|
||||
// override fun transformPropertyName(
|
||||
// propertyName: String,
|
||||
// property: KProperty<*>,
|
||||
// klass: KClass<*>
|
||||
// ): String {
|
||||
// return camelCaseToSnakeCase(propertyName)
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue