project re-organisation, and rename

This commit is contained in:
Adam 2021-12-01 16:57:46 +01:00
parent 53862ef1b5
commit 633e898f3c
52 changed files with 525 additions and 387 deletions

View file

@ -0,0 +1,97 @@
@file:Suppress("ClassName")
package dev.adamko.factorioevents.model
import kotlinx.serialization.Serializable
/*
* The basic types used in the Factorio API. While Lua only supports the `boolean`, `number`,
* `string`, and `table` types (as they relate to the types listed below), the API docs will
* specify what C++ types these are converted to or from internally.
*/
/**
* Tables are enclosed in curly brackets, like this `{}`
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#table)
*/
typealias F_table = MutableMap<Any, Any>
sealed interface FBuiltInType {
sealed interface FNumber {
/**
* A floating-point number. This is a single-precision floating point number. Whilst Lua only uses double-precision
* numbers, when a function takes a float, the game engine will immediately convert the double-precision number to
* single-precision.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#float)
*/
@Serializable
@JvmInline
value class F_float(val float: Float) : FNumber
/**
* A double-precision floating-point number. This is the same data type as all Lua numbers use.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#double)
*/
@Serializable
@JvmInline
value class F_double(val double: Double) : FNumber
/**
* 32-bit signed integer. Possible values are -2,147,483,648 to 2,147,483,647.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#int)
*/
@Serializable
@JvmInline
value class F_int(val int: Int) : FNumber
/**
* 8-bit signed integer. Possible values are -128 to 127.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#int8)
*/
@Serializable
@JvmInline
value class F_int8(val int8: Byte) : FNumber
/**
* 32-bit unsigned integer. Possible values are 0 to 4,294,967,295.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#uint)
*/
@Serializable
@JvmInline
value class F_uint(val uint: UInt) : FNumber
/**
* 8-bit unsigned integer. Possible values are 0 to 255.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#uint8)
*/
@Serializable
@JvmInline
value class F_uint8(val uint8: UByte) : FNumber
/**
* 16-bit unsigned integer. Possible values are 0 to 65535.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#uint16)
*/
@Serializable
@JvmInline
value class F_uint16(val uint16: UShort) : FNumber
/**
* 64-bit unsigned integer. Possible values are 0 to 18,446,744,073,709,551,615.
*
* [View documentation](https://lua-api.factorio.com/next/Builtin-Types.html#uint64)
*/
@Serializable
@JvmInline
value class F_uint64(val uint64: ULong) : FNumber
}
}

View file

@ -0,0 +1,101 @@
package dev.adamko.factorioevents.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator
//@Serializable(with = FactorioEventSerializer::class)
@Serializable
data class FactorioEvent<out T : FactorioObjectData>(
/** the initial Factorio event ({defines.events}) trigger */
@SerialName("event_type")
val eventType: String,
/** Schema versioning */
@SerialName("mod_version")
val modVersion: 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 {
@SerialName(FactorioObjectDataDiscriminatorKey)
abstract val objectName: String
}
@Serializable
@SerialName("LuaPlayer")
data class PlayerData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
@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,
) : FactorioObjectData()
@Serializable
@SerialName("LuaEntity")
data class EntityData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
@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?,
@SerialName("player_index")
val playerIndex: UInt? = null,
) : FactorioObjectData()
@Serializable
@SerialName("LuaSurface")
data class SurfaceData(
@SerialName(FactorioObjectDataDiscriminatorKey)
override val objectName: String,
@SerialName("daytime")
val daytime: Double,
@SerialName("index")
val index: UInt,
@SerialName("name")
val name: String,
) : FactorioObjectData()
@Serializable
data class PositionData(
@SerialName("x")
val x: Double,
@SerialName("y")
val y: Double,
)

View file

@ -0,0 +1,34 @@
package dev.adamko.factorioevents.model
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.modules.SerializersModule
/** Factorio outputs lists as Json objects - this serializer converts an object back to a list. */
class ListAsObjectSerializer<T>(dataSerializer: KSerializer<T>) :
JsonTransformingSerializer<List<T>>(ListSerializer(dataSerializer)) {
override fun transformDeserialize(element: JsonElement): JsonElement =
JsonArray(
element.jsonObject.values.toList()
)
override fun transformSerialize(element: JsonElement): JsonElement =
JsonObject(
element.jsonArray.mapIndexed { index, jsonElement -> "$index" to jsonElement }.toMap()
)
}
val jsonMapper = Json {
prettyPrint = true
prettyPrintIndent = " "
serializersModule = SerializersModule { }
}

View file

@ -0,0 +1,70 @@
package dev.adamko.factorioevents.model
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import me.ntrrgc.tsGenerator.ClassTransformer
import me.ntrrgc.tsGenerator.TypeScriptGenerator
import me.ntrrgc.tsGenerator.camelCaseToSnakeCase
import org.intellij.lang.annotations.Language
fun main(args: Array<String>) {
val gen =
TypeScriptGenerator(
rootClasses =
buildSet {
add(FactorioEvent::class)
addAll(FactorioObjectData::class.sealedSubclasses)
},
mappings = mapOf(
// builtin Factorio numeric types > `typed-factorio/generated/builtin-types.d.ts`
Float::class to "float",
Double::class to "double",
Int::class to "int",
Byte::class to "int8",
UInt::class to "uint",
UByte::class to "uint8",
UShort::class to "uint16",
ULong::class to "uint64",
),
classTransformers = listOf(
PropertyNameTransformer,
)
)
val definitions = gen.definitionsText
when (val filename = args.firstOrNull()) {
null -> {
println(definitions)
}
else -> {
val out = File(filename)
out.parentFile.mkdirs()
out.printWriter().use {
it.print(header)
it.print(definitions)
}
println("Generated file: $out")
}
}
}
@Language("TypeScript")
val header = """
// Generated code - do edit this file manually
""".trimIndent()
object PropertyNameTransformer : ClassTransformer {
override fun transformPropertyName(
propertyName: String,
property: KProperty<*>,
klass: KClass<*>
): String {
return camelCaseToSnakeCase(propertyName)
}
}

View file

@ -0,0 +1,139 @@
package dev.adamko.factorioevents.model
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.equality.shouldBeEqualToComparingFields
import kotlinx.serialization.decodeFromString
import org.intellij.lang.annotations.Language
class FactorioEventTest : BehaviorSpec({
Given("json on_player_joined_game") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaPlayer",
"name": "fredthedeadhead",
"character_unit_number": 1,
"associated_characters_unit_numbers": {},
"position": {
"x": -3.5703125,
"y": 29.75
}
},
"event_type": "on_player_joined_game",
"mod_version": "0.0.4",
"tick": 1278458
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = PlayerData(
objectName = "LuaPlayer",
name = "fredthedeadhead",
characterUnitNumber = 1u,
associatedCharactersUnitNumbers = emptyList(),
position = PositionData(
x = -3.5703125,
y = 29.75
)
),
eventType = "on_player_joined_game",
modVersion = "0.0.4",
tick = 1278458u
)
actual shouldBeEqualToComparingFields expected
}
}
Given("json surface") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaSurface",
"name": "nauvis",
"index": 1,
"daytime": 0.8390800000511436707029133685864508152008056640625
},
"event_type": "on_tick",
"mod_version": "0.0.4",
"tick": 1278480
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = SurfaceData(
objectName = "LuaSurface",
name = "nauvis",
index = 1u,
daytime = 0.8390800000511436707029133685864508152008056640625
),
eventType = "on_tick",
modVersion = "0.0.4",
tick = 1278480u
)
actual shouldBeEqualToComparingFields expected
}
}
Given("json entity") {
@Language("JSON")
val json = """
{
"data": {
"object_name": "LuaEntity",
"name": "character",
"type": "character",
"active": true,
"health": 250,
"health_ratio": 1,
"surface_index": 1,
"unit_number": 1,
"position": {
"x": -0.25,
"y": 30.09375
}
},
"event_type": "on_player_changed_position",
"mod_version": "0.0.6",
"tick": 1384507
}
""".trimIndent()
Then("parse") {
val actual: FactorioEvent<FactorioObjectData> = jsonMapper.decodeFromString(json)
val expected = FactorioEvent(
data = EntityData(
objectName = "LuaEntity",
name = "character",
type = "character",
active = true,
health = 250.0,
healthRatio = 1.0,
surfaceIndex = 1,
unitNumber = 1u,
playerIndex = null,
position = PositionData(
x = -0.25,
y = 30.09375,
)
),
eventType = "on_player_changed_position",
modVersion = "0.0.6",
tick = 1384507u
)
actual shouldBeEqualToComparingFields expected
}
}
})