diff --git a/build.gradle.kts b/build.gradle.kts index 42a664f..2b60f01 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "dev.adamko.kafkatorio" -version = "0.1.7" +version = "0.2.0" val licenseFile: RegularFile by extra(layout.projectDirectory.file("LICENSE")) diff --git a/modules/events-mod/build.gradle.kts b/modules/events-mod/build.gradle.kts index 12719aa..12fee4b 100644 --- a/modules/events-mod/build.gradle.kts +++ b/modules/events-mod/build.gradle.kts @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs plugins { idea - id("dev.adamko.kafkatorio.lang.node") + dev.adamko.kafkatorio.lang.node distribution } @@ -60,7 +60,7 @@ val typescriptToLua by tasks.registering(NpmTask::class) { description = "Convert Typescript To Lua" group = project.name - dependsOn(tasks.npmInstall, installEventsTsSchema) + dependsOn(tasks.npmInstall, installEventsTsSchema, tasks.updatePackageJson) execOverrides { standardOutput = System.out } @@ -214,5 +214,3 @@ tasks.assemble { dependsOn(installEventsTsSchema, tasks.updatePackageJson) } // } // } // } - -tasks.npmInstall diff --git a/modules/events-mod/src/main/typescript/control.ts b/modules/events-mod/src/main/typescript/control.ts index e0064ea..5448f13 100644 --- a/modules/events-mod/src/main/typescript/control.ts +++ b/modules/events-mod/src/main/typescript/control.ts @@ -1,8 +1,5 @@ -import {emitTiles} from "./prototypes/emitPrototypes"; +import {handleConfigurationUpdate} from "./main/config-update/handleConfigurationUpdate"; require("events") -// on startup, push out prototypes -script.on_configuration_changed((e: ConfigurationChangedData) => { - emitTiles() -}) +script.on_configuration_changed(handleConfigurationUpdate) diff --git a/modules/events-mod/src/main/typescript/events/emitter.ts b/modules/events-mod/src/main/typescript/events/emitter.ts index 8613fae..28a8f8e 100644 --- a/modules/events-mod/src/main/typescript/events/emitter.ts +++ b/modules/events-mod/src/main/typescript/events/emitter.ts @@ -9,9 +9,8 @@ export function emitEvent(eventData: T, tick: uint let event: FactorioEvent = { data: eventData, - event_type: eventType, - mod_version: MOD_VERSION, - factorio_version: FACTORIO_VERSION, + eventType: eventType, + modVersion: MOD_VERSION, tick: tick } diff --git a/modules/events-mod/src/main/typescript/events/handlers.ts b/modules/events-mod/src/main/typescript/events/handlers.ts index e2ec935..0bcf505 100644 --- a/modules/events-mod/src/main/typescript/events/handlers.ts +++ b/modules/events-mod/src/main/typescript/events/handlers.ts @@ -50,10 +50,12 @@ export function handleConsoleChat( const CHUNK_SIZE = 32 -export function handleChunkUpdate(tick: uint, - eventType: string, - surfaceIndex: uint, - position: ChunkPosition) { +export function handleChunkUpdate( + tick: uint, + eventType: string, + surfaceIndex: uint, + position: ChunkPosition +) { let surface = game.get_surface(surfaceIndex) @@ -69,14 +71,13 @@ export function handleChunkUpdate(tick: uint, let tilePositionMap = new Map(); for (let tile of tiles) { - let position = Serdes.positionTableToTable(tile.position) + let position = Serdes.convertMapPosition(tile.position) tilePositionMap.set(position, Serdes.convertTile(tile)) } let mapChunk: FactorioTilesMap = { - object_name: "FactorioTilesMap", + objectName: "FactorioTilesMap", tiles: tilePositionMap, } emitEvent(mapChunk, tick, eventType) - } diff --git a/modules/events-mod/src/main/typescript/events/serdes.ts b/modules/events-mod/src/main/typescript/events/serdes.ts index 3394ba6..4eb7e42 100644 --- a/modules/events-mod/src/main/typescript/events/serdes.ts +++ b/modules/events-mod/src/main/typescript/events/serdes.ts @@ -3,15 +3,15 @@ export namespace Serdes { export function playerToTable(player: LuaPlayer): PlayerData { let charIds = entitiesToUnitNumbers(player.get_associated_characters()) return { - object_name: player.object_name, + objectName: player.object_name, name: player.name, - character_unit_number: player.character?.unit_number ?? null, - associated_characters_unit_numbers: charIds, - position: positionTableToTable(player.position), + characterUnitNumber: player.character?.unit_number ?? null, + associatedCharactersUnitNumbers: charIds, + position: convertMapPosition(player.position), colour: mapColour(player.color), - chat_colour: mapColour(player.chat_color), - last_online: player.last_online + chatColour: mapColour(player.chat_color), + lastOnline: player.last_online } } @@ -20,20 +20,20 @@ export namespace Serdes { let player: LuaPlayer | null = entity.is_player() ? entity.player!! : null return { - object_name: entity.object_name, + objectName: entity.object_name, // entity data name: entity.name, type: entity.type, active: entity.active, health: entity.health ?? null, - health_ratio: entity.get_health_ratio(), - surface_index: entity.surface.index, - unit_number: entity.unit_number ?? null, - position: positionTableToTable(entity.position), + healthRatio: entity.get_health_ratio(), + surfaceIndex: entity.surface.index, + unitNumber: entity.unit_number ?? null, + position: convertMapPosition(entity.position), // player data - player_index: player?.index ?? null + playerIndex: player?.index ?? null } } @@ -49,7 +49,7 @@ export namespace Serdes { export function surfaceToTable(surface: LuaSurface): SurfaceData { return { - object_name: surface.object_name, + objectName: surface.object_name, name: surface.name, index: surface.index, @@ -57,10 +57,11 @@ export namespace Serdes { } } - export function positionTableToTable(positionTable: PositionTable): PositionData { + export function convertMapPosition(mapPos: MapPositionTable): PositionData { return { - x: positionTable.x, - y: positionTable.y, + type: "MAP", + x: mapPos.x, + y: mapPos.y, } } @@ -75,20 +76,20 @@ export namespace Serdes { export function consoleChat(content: string, playerIndex?: uint): ConsoleChatMessage { return { - object_name: "ConsoleChatMessage", + objectName: "ConsoleChatMessage", - author_player_index: playerIndex ?? null, + authorPlayerIndex: playerIndex ?? null, content: content } } export function convertTile(tile: LuaTile): FactorioTile { return { - object_name: tile.object_name, + objectName: tile.object_name, - position: tile.position, - prototype_name: tile.prototype.name, - surface_index: tile.surface.index + position: convertMapPosition(tile.position), + prototypeName: tile.prototype.name, + surfaceIndex: tile.surface.index } } diff --git a/modules/events-mod/src/main/typescript/main/config-update/handleConfigurationUpdate.ts b/modules/events-mod/src/main/typescript/main/config-update/handleConfigurationUpdate.ts new file mode 100644 index 0000000..1ea1d0e --- /dev/null +++ b/modules/events-mod/src/main/typescript/main/config-update/handleConfigurationUpdate.ts @@ -0,0 +1,43 @@ +import {emitPacket} from "../emitKafkatorioPacket"; +import {convertPrototypes} from "../../prototypes/emitPrototypes"; + +export function handleConfigurationUpdate(changeData: ConfigurationChangedData) { + + emitPacket({ + modVersion: global.MOD_VERSION, + allMods: allMods(changeData), + factorioData: factorioData(changeData), + prototypes: convertPrototypes() + }) + +} + +function factorioData(changeData: ConfigurationChangedData): FactorioGameDataUpdate { + return { + oldVersion: changeData.old_version ?? null, + newVersion: changeData.new_version ?? null, + } +} + +function allMods(e: ConfigurationChangedData): FactorioModInfo[] { + + let namesOfChangedMods: string[] = Object.keys(e.mod_changes) + let namesOfCurrentMods: string[] = Object.keys(script.active_mods) + + let modNames = [...new Set( + namesOfChangedMods.concat(namesOfCurrentMods) + )] + + return modNames.map((modName) => { + + let currentVer = script.active_mods[modName] ?? e.mod_changes[modName].new_version + let previousVer = e.mod_changes[modName].old_version + + return { + modName: modName, + currentVersion: currentVer, + previousVersion: previousVer, + } + }) + +} diff --git a/modules/events-mod/src/main/typescript/main/config-update/updateGlobal.ts b/modules/events-mod/src/main/typescript/main/config-update/updateGlobal.ts new file mode 100644 index 0000000..db1e621 --- /dev/null +++ b/modules/events-mod/src/main/typescript/main/config-update/updateGlobal.ts @@ -0,0 +1,4 @@ +export function updateGlobal() { + global.MOD_VERSION = script.active_mods[script.mod_name][1] + global.FACTORIO_VERSION = script.active_mods["base"][1] +} diff --git a/modules/events-mod/src/main/typescript/main/emitKafkatorioPacket.ts b/modules/events-mod/src/main/typescript/main/emitKafkatorioPacket.ts new file mode 100644 index 0000000..c86af5d --- /dev/null +++ b/modules/events-mod/src/main/typescript/main/emitKafkatorioPacket.ts @@ -0,0 +1,6 @@ +/** Emit a serialised KafkatorioPacket */ +export function emitPacket(packet: T) { + let data = game.table_to_json(packet) + localised_print(`KafkatorioPacket: ${data}`) + // rcon.print(`KafkatorioPacket: ${data}`) +} diff --git a/modules/events-mod/src/main/typescript/main/global.d.ts b/modules/events-mod/src/main/typescript/main/global.d.ts new file mode 100644 index 0000000..7a73643 --- /dev/null +++ b/modules/events-mod/src/main/typescript/main/global.d.ts @@ -0,0 +1,4 @@ +declare const global: { + MOD_VERSION: string + FACTORIO_VERSION: string +} diff --git a/modules/events-mod/src/main/typescript/package-lock.json b/modules/events-mod/src/main/typescript/package-lock.json index 4fe36f3..e30a5b3 100644 --- a/modules/events-mod/src/main/typescript/package-lock.json +++ b/modules/events-mod/src/main/typescript/package-lock.json @@ -1,6 +1,6 @@ { "name": "kafkatorio-events-mod", - "version": "0.1.7", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/modules/events-mod/src/main/typescript/package.json b/modules/events-mod/src/main/typescript/package.json index 5fd898c..e708d32 100644 --- a/modules/events-mod/src/main/typescript/package.json +++ b/modules/events-mod/src/main/typescript/package.json @@ -1,6 +1,6 @@ { "name": "kafkatorio-events-mod", - "version": "0.1.7", + "version": "0.2.0", "private": true, "scripts": { "build": "tstl", diff --git a/modules/events-mod/src/main/typescript/prototypes/emitPrototypes.ts b/modules/events-mod/src/main/typescript/prototypes/emitPrototypes.ts index 44aa893..2099e3e 100644 --- a/modules/events-mod/src/main/typescript/prototypes/emitPrototypes.ts +++ b/modules/events-mod/src/main/typescript/prototypes/emitPrototypes.ts @@ -1,42 +1,36 @@ import {Serdes} from "../events/serdes"; -export function emitTiles() { +export function convertPrototypes (): FactorioPrototype[] { + let prototypes = new Array() - let tilePrototypes = new LuaTable() + prototypes.push(...getMapTilePrototypes()) - for (const [_, tile] of pairs(game.tile_prototypes)) { - // let ttttt = game.tile_prototypes[tileName] - tilePrototypes.set(tile.name, mapTilePrototype(tile)) + return prototypes +} + +function getMapTilePrototypes(): FactorioMapTilePrototype[] { + let tiles: FactorioMapTilePrototype[] = [] + for (let [_, tile] of pairs(game.tile_prototypes)) { + tiles.push( + { + objectName: tile.object_name, + + name: tile.name, + order: tile.order, + layer: tile.layer, + collisionMasks: convertCollisionMaskToNames(tile.collision_mask), + mapColor: Serdes.mapColour(tile.map_color), + canBeMined: tile.mineable_properties.minable, + } + ) } - - // for (let tileName in game.tile_prototypes) { - // const tile = game.tile_prototypes[tileName] - // tilePrototypes.set(tileName, mapTilePrototype(tile)) - // } - - // for (const [id, player] of pairs(game.players)) { - // const num: number = id - // const p: LuaPlayer = player - // } - emitPrototypes(tilePrototypes) + return tiles } -function mapTilePrototype(tile: LuaTilePrototype): FactorioMapTilePrototype { - return { - object_name: tile.object_name, - - name: tile.name, - order: tile.order, - layer: tile.layer, - collision_masks: [], - map_color: Serdes.mapColour(tile.map_color), - can_be_mined: tile.mineable_properties.minable, +function convertCollisionMaskToNames(cm: CollisionMask): string[] { + let masks: string[] = [] + for (let [name, _] of pairs(cm)) { + masks.push(name) } -} - -function emitPrototypes(prototypes: LuaTable) { - - let data = game.table_to_json(prototypes) - - localised_print(`FactorioPrototype: ${data}`) + return masks } diff --git a/modules/events-mod/src/main/typescript/schema/config-update.d.ts b/modules/events-mod/src/main/typescript/schema/config-update.d.ts new file mode 100644 index 0000000..1f8e37a --- /dev/null +++ b/modules/events-mod/src/main/typescript/schema/config-update.d.ts @@ -0,0 +1,19 @@ +// Generated by TypeScriptGenerator - do not edit this file manually + +interface FactorioModInfo { + currentVersion: string | null; + modName: string; + previousVersion: string | null; +} + +interface FactorioGameDataUpdate { + newVersion: string | null; + oldVersion: string | null; +} + +interface FactorioConfigurationUpdate extends KafkatorioPacket { + allMods: FactorioModInfo[]; + factorioData: FactorioGameDataUpdate; + modVersion: string; + prototypes: FactorioPrototype[]; +} diff --git a/modules/events-mod/src/main/typescript/schema/events-schema.ts b/modules/events-mod/src/main/typescript/schema/events-schema.ts deleted file mode 100644 index 24d67a3..0000000 --- a/modules/events-mod/src/main/typescript/schema/events-schema.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Generated by TypeScriptGenerator - do not edit this file manually -interface FactorioObjectData { - object_name: string; -} - -interface FactorioEvent { - data: T; - event_type: string; - factorio_version: string; - mod_version: string; - tick: uint; -} - -interface Colour { - alpha: float; - blue: float; - green: float; - red: float; -} - -interface PositionData { - x: double; - y: double; -} - -interface PlayerData extends FactorioObjectData { - associated_characters_unit_numbers: uint[]; - character_unit_number: uint | null; - chat_colour: Colour; - colour: Colour; - last_online: uint; - name: string; - object_name: string; - position: PositionData; -} - -interface EntityData extends FactorioObjectData { - active: boolean; - health: double | null; - health_ratio: double; - name: string; - object_name: string; - player_index: uint | null; - position: PositionData; - surface_index: int; - type: string; - unit_number: uint | null; -} - -interface SurfaceData extends FactorioObjectData { - daytime: double; - index: uint; - name: string; - object_name: string; -} - -interface ConsoleChatMessage extends FactorioObjectData { - author_player_index: uint | null; - content: string; - object_name: string; -} - -interface FactorioTile extends FactorioObjectData { - object_name: string; - position: PositionData; - prototype_name: string; - surface_index: int; -} - -interface FactorioTilesMap extends FactorioObjectData { - object_name: string; - tiles: Map; -} - -interface FactorioTiles extends FactorioObjectData { - object_name: string; - tiles: FactorioTile[]; -} - -interface FactorioPrototype { - object_name: string; -} - -interface FactorioMapTilePrototype extends FactorioPrototype { - can_be_mined: boolean; - collision_masks: string[]; - layer: uint; - map_color: Colour; - name: string; - object_name: string; - order: string; -} \ No newline at end of file diff --git a/modules/events-mod/src/main/typescript/schema/object-data.d.ts b/modules/events-mod/src/main/typescript/schema/object-data.d.ts new file mode 100644 index 0000000..612b721 --- /dev/null +++ b/modules/events-mod/src/main/typescript/schema/object-data.d.ts @@ -0,0 +1,61 @@ +// Generated by TypeScriptGenerator - do not edit this file manually + +interface FactorioObjectData { + objectName: string; +} + +interface FactorioEvent extends KafkatorioPacket { + data: T; + eventType: string; + modVersion: string; + tick: uint; +} + +interface PlayerData extends FactorioObjectData { + associatedCharactersUnitNumbers: uint[]; + characterUnitNumber: uint | null; + chatColour: Colour; + colour: Colour; + lastOnline: uint; + name: string; + objectName: string; + position: PositionData; +} + +interface EntityData extends FactorioObjectData { + active: boolean; + health: double | null; + healthRatio: double; + name: string; + objectName: string; + playerIndex: uint | null; + position: PositionData; + surfaceIndex: int; + type: string; + unitNumber: uint | null; +} + +interface SurfaceData extends FactorioObjectData { + daytime: double; + index: uint; + name: string; + objectName: string; +} + +interface FactorioTile extends FactorioObjectData { + objectName: string; + position: PositionData; + prototypeName: string; + surfaceIndex: int; +} + +interface FactorioTilesMap extends FactorioObjectData { + objectName: string; + tiles: Map; +} + +interface ConsoleChatMessage extends FactorioObjectData { + authorPlayerIndex: uint | null; + content: string; + objectName: string; +} diff --git a/modules/events-mod/src/main/typescript/schema/prototype.d.ts b/modules/events-mod/src/main/typescript/schema/prototype.d.ts new file mode 100644 index 0000000..f1d2052 --- /dev/null +++ b/modules/events-mod/src/main/typescript/schema/prototype.d.ts @@ -0,0 +1,20 @@ +// Generated by TypeScriptGenerator - do not edit this file manually + +interface FactorioPrototype { + objectName: string; +} + +interface FactorioPrototypes extends KafkatorioPacket { + modVersion: string; + prototypes: T[]; +} + +interface FactorioMapTilePrototype extends FactorioPrototype { + canBeMined: boolean; + collisionMasks: string[]; + layer: uint; + mapColor: Colour; + name: string; + objectName: string; + order: string; +} diff --git a/modules/events-mod/src/main/typescript/schema/schema.d.ts b/modules/events-mod/src/main/typescript/schema/schema.d.ts new file mode 100644 index 0000000..9738c8c --- /dev/null +++ b/modules/events-mod/src/main/typescript/schema/schema.d.ts @@ -0,0 +1,20 @@ +// Generated by TypeScriptGenerator - do not edit this file manually + +interface KafkatorioPacket { + modVersion: string; +} + +interface Colour { + alpha: float; + blue: float; + green: float; + red: float; +} + +type PositionType = "MAP" | "CHUNK" | "TILE"; + +interface PositionData { + type: PositionType | null; + x: double; + y: double; +} diff --git a/modules/events-schema/build.gradle.kts b/modules/events-schema/build.gradle.kts index efe3409..b19af7d 100644 --- a/modules/events-schema/build.gradle.kts +++ b/modules/events-schema/build.gradle.kts @@ -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) diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioCommonData.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioCommonData.kt new file mode 100644 index 0000000..191e215 --- /dev/null +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioCommonData.kt @@ -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, +//) : FactorioObjectData() { +// @Transient +// override val objectName: String = "FactorioTiles" +//} diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt new file mode 100644 index 0000000..a3c829f --- /dev/null +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioConfiguration.kt @@ -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, + val prototypes: List, +) : KafkatorioPacket + +@Serializable +data class FactorioGameDataUpdate( + val oldVersion: String?, + val newVersion: String?, +) + +@Serializable +data class FactorioModInfo( + val modName: String, + val currentVersion: String?, + val previousVersion: String?, +) diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt index 746352d..15eef33 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioEvents.kt @@ -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( + 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()?.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, - @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, -) : FactorioObjectData() { - @Transient - override val objectName: String = "FactorioTilesMap" -} - - -@Serializable -@SerialName("FactorioTiles") -data class FactorioTiles( - val tiles: List, -) : 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 diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt new file mode 100644 index 0000000..0ab2686 --- /dev/null +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioLuaObjects.kt @@ -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()?.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, + 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, +) : 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" +} diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioPrototypes.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioPrototypes.kt index 53aacb6..70404b9 100644 --- a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioPrototypes.kt +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/factorioPrototypes.kt @@ -6,9 +6,19 @@ import kotlinx.serialization.Transient import kotlinx.serialization.json.JsonClassDiscriminator @Serializable -@JsonClassDiscriminator(FactorioObjectDataDiscriminatorKey) +class FactorioPrototypes( + override val modVersion: String, + val prototypes: List, +) : 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, val order: String, - /** Is this tile mineable at all? */ + /** Can the tile be mined for resources? */ val canBeMined: Boolean, ) : FactorioPrototype() { @Transient diff --git a/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt new file mode 100644 index 0000000..194f2c6 --- /dev/null +++ b/modules/events-schema/src/commonMain/kotlin/dev/adamko/kafkatorio/events/schema/kafkatorioPacket.kt @@ -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" +// } +//} diff --git a/modules/events-schema/src/jvmMain/kotlin/dev/adamko/kafkatorio/events/schema/kt2ts.kt b/modules/events-schema/src/jvmMain/kotlin/dev/adamko/kafkatorio/events/schema/kt2ts.kt index 8bbbabf..c1b48ab 100644 --- a/modules/events-schema/src/jvmMain/kotlin/dev/adamko/kafkatorio/events/schema/kt2ts.kt +++ b/modules/events-schema/src/jvmMain/kotlin/dev/adamko/kafkatorio/events/schema/kt2ts.kt @@ -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) { 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) { ULong::class to "uint64", ), classTransformers = listOf( - PropertyNameTransformer, +// PropertyNameTransformer, ) ) @@ -44,29 +40,60 @@ fun main(args: Array) { 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 { + 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) +// } +//}