diff --git a/README.md b/README.md index e69de29..9606faa 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,22 @@ +1. output events from Factorio into Kafka. + 1. Factorio mod listens to events. + 2. For each event, it emits a file. + 3. A Kafka Connector consumes each file. +2. Factorio Web Map reads data from Kafka + 1. converts data to images + 2. hosts a web map + +todo + +* add settings + * is active - whether files are exported or not + * delete files - clean up all files + * tick-rate - how often reports are generated ( + * requires on some sort of in-mod cache of map) + * also divide by game-speed? to keep ratio the same + +to investigate: + +* https://github.com/starwing/lua-protobuf +* https://github.com/grafi-tt/lunajson + diff --git a/buildSrc/src/main/kotlin/dev/adamko/factoriowebmap/archetype/kotlin-js.gradle.kts b/buildSrc/src/main/kotlin/dev/adamko/factoriowebmap/archetype/kotlin-js.gradle.kts index 2dcac1b..879e2df 100644 --- a/buildSrc/src/main/kotlin/dev/adamko/factoriowebmap/archetype/kotlin-js.gradle.kts +++ b/buildSrc/src/main/kotlin/dev/adamko/factoriowebmap/archetype/kotlin-js.gradle.kts @@ -19,27 +19,22 @@ plugins { kotlin { js(IR) { - binaries.executable() useCommonJs() nodejs() -// -//// compilations.all { -// compilations["main"].apply { -// packageJson { -// customField( -// "scripts", -// mapOf( -// "build" to "tstl", -// "dev" to "tstl --watch", -// ) -// ) -// } -// } } } +dependencies { + val kotlinWrappersVersion = "0.0.1-pre.259-kotlin-1.5.31" + implementation( + project.dependencies.enforcedPlatform( + "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${kotlinWrappersVersion}" + ) + ) +} + val rootPackageJson by rootProject.tasks.getting(RootPackageJsonTask::class) //fun NpmTask.setNodeModulesPath(path: String): Unit = diff --git a/modules/mod/build.gradle.kts b/modules/mod/build.gradle.kts index 3634cba..41c79e4 100644 --- a/modules/mod/build.gradle.kts +++ b/modules/mod/build.gradle.kts @@ -103,7 +103,8 @@ val serverStopTask = tasks.register("dockerStop") { val serverUpTask = tasks.register("dockerUp") { group = "$projectId.factorioServer" - dependsOn(copyModToServerTask, serverStopTask) + mustRunAfter(copyModToServerTask) + dependsOn(serverStopTask) workingDir = layout.projectDirectory.dir("src/test/resources/server/").asFile commandLine = parseSpaceSeparatedArgs("docker-compose up -d") diff --git a/modules/mod/src/main/typescript/events.ts b/modules/mod/src/main/typescript/events.ts index 67dee7d..5d07a8d 100644 --- a/modules/mod/src/main/typescript/events.ts +++ b/modules/mod/src/main/typescript/events.ts @@ -8,39 +8,80 @@ // declare const factorioData: Data // declare const factorioMods: Mods - // const eventEnum = { // thingType: Object.values(defines.events) // } -const SERVER_ID : uint = 0 +import {JsonTable, Serdes} from "./serdes/serdes" -function outputEvent(filename: string, event: table) { - let json : string = game.table_to_json(event) - // game.print(`Creating file ${filename}, content: ${json}`) - game.write_file(filename, json, false, SERVER_ID) +// const SERVER_ID: uint = 0 +// const EVENT_FILE_DIR: string = "events" + +function playerEvent(tick: uint, player_index: uint, eventType: string) { + let player: LuaPlayer = game.players[player_index] + + let filename: string = table.concat([player.index, eventType, tick], "_") + let data = Serdes.Player.playerToTable(player) + outputEventFile(player.object_name + "/" + filename, data) + + if (player.character != undefined) { + entityEvent(tick, player.character, eventType) + + for (const char of player.get_associated_characters()) { + if (char != undefined) { + entityEvent(tick, char, eventType) + } + } + + } } -script.on_event( - [defines.events.on_pre_build, defines.events.on_player_dropped_item], - (e: OnPreBuildEvent | OnPlayerDroppedItemEvent) => { - game.print("player " + e.player_index + " dropped item, tick:" + e.tick) - } -); +function entityEvent(tick: uint, entity: LuaEntity, eventType: string) { + if (entity.unit_number != undefined && entity.unit_number != 0) { + let filename: string = table.concat([entity.unit_number, eventType, tick], "_") + let data = Serdes.Entity.entityToTable(entity) + outputEventFile(entity.object_name + "/" + filename, data) + } +} +function surfaceEvent(tick: uint, surface: LuaSurface, eventType: string) { + let filename: string = + surface.object_name + "/" + table.concat([surface.index, eventType, tick], "_") + let data = Serdes.Surface.surfaceToTable(surface) + outputEventFile(filename, data) +} + +function outputEventFile(filename: string, obj: JsonTable) { + let data = game.table_to_json(obj) + // game.write_file(EVENT_FILE_DIR + "/" + filename, data, false, SERVER_ID) + localised_print(`!===== ${filename} | ${data} =====!`) +} + +// script.on_event( +// [defines.events.on_pre_build, defines.events.on_player_dropped_item], +// (e: OnPreBuildEvent | OnPlayerDroppedItemEvent) => { +// game.print("player " + e.player_index + " dropped item, tick:" + e.tick) +// } +// ); + +script.on_event( + defines.events.on_player_mined_entity, + (e: OnPlayerMinedEntityEvent) => { + entityEvent(e.tick, e.entity, "on_player_mined_entity") + playerEvent(e.tick, e.player_index, "on_player_mined_entity") + } +) script.on_event( defines.events.on_player_joined_game, (e: OnPlayerJoinedGameEvent) => { - let filename : string = `${e.name}/${e.tick}_${e.player_index}` - outputEvent(filename, e) + playerEvent(e.tick, e.player_index, "on_player_joined_game") } ) script.on_event( defines.events.on_player_changed_position, - (e: OnPlayerJoinedGameEvent) => { - let filename : string = `${e.name}/${e.tick}_${e.player_index}` - outputEvent(filename, e) + (e: OnPlayerChangedPositionEvent) => { + playerEvent(e.tick, e.player_index, "on_player_changed_position") } ) @@ -48,10 +89,19 @@ script.on_event( defines.events.on_tick, (e: OnTickEvent) => { if (e.tick % 60 == 0) { - let msg = "test mod tick: " + e.tick + "\n" - game.write_file("tick.log", msg, true, SERVER_ID) - let filename : string = `${e.name}/${e.tick}` - outputEvent(filename, e) + + game.surfaces + + for (const [index,] of pairs(game.surfaces)) { + let surface = game.surfaces[index] + surfaceEvent(e.tick, surface, "on_tick") + } + + // let filename: string = table.concat([entity.unit_number, eventType, tick], "_") + // let msg = "test mod tick: " + e.tick + "\n" + // game.write_file("tick.log", msg, true, SERVER_ID) + // let filename: string = `OnTickEvent/${e.tick}` + // outputEvent(filename, e) } } ); diff --git a/modules/mod/src/main/typescript/ext/LuaArray.ts b/modules/mod/src/main/typescript/ext/LuaArray.ts new file mode 100644 index 0000000..e24e759 --- /dev/null +++ b/modules/mod/src/main/typescript/ext/LuaArray.ts @@ -0,0 +1,16 @@ +// declare interface LuaArray extends LuaTable { +// } + +// = LuaTable; + +// declare type LuaArrayConstructor = (new () => LuaArray) + +// declare const LuaArray: LuaArrayConstructor + +// class LuaArray extends LuaTable { +// add(val: TValue) { +// this.set(this.length(), val) +// } +// } + +// declare type LuaArray & LuaTable \ No newline at end of file diff --git a/modules/mod/src/main/typescript/package-lock.json b/modules/mod/src/main/typescript/package-lock.json index 8385974..6aea416 100644 --- a/modules/mod/src/main/typescript/package-lock.json +++ b/modules/mod/src/main/typescript/package-lock.json @@ -74,9 +74,9 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, "typed-factorio": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/typed-factorio/-/typed-factorio-0.7.3.tgz", - "integrity": "sha512-Y0J6dqRy1rg3kPlFUX5Ca5nU7CcQgelcY4nKkk6fox17ZCAoOZx1i2ynj/JPuhQsTRsuMV0kQTS1eQKF8bzdaQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/typed-factorio/-/typed-factorio-0.7.4.tgz", + "integrity": "sha512-IhvydBF1eNtlV8WRJWuu64ySOoMCbNujLX/kvqNMaWL+tcLmORghuQZggCbhuX2HI4wVL+UgGpTwG4xicOQM9A==", "requires": { "lua-types": "^2.10.1", "typescript-to-lua": "^1.0.0" diff --git a/modules/mod/src/main/typescript/package.json b/modules/mod/src/main/typescript/package.json index d6f981f..d6f6e1b 100644 --- a/modules/mod/src/main/typescript/package.json +++ b/modules/mod/src/main/typescript/package.json @@ -14,7 +14,7 @@ "lua-types": "^2.11.0", "@types/node": "^12.0.8", "typescript-to-lua": "1.0.1", - "typed-factorio": "0.7.3" + "typed-factorio": "0.7.4" }, "peerDependencies": {}, "optionalDependencies": {}, diff --git a/modules/mod/src/main/typescript/serdes/serdes.ts b/modules/mod/src/main/typescript/serdes/serdes.ts new file mode 100644 index 0000000..2396228 --- /dev/null +++ b/modules/mod/src/main/typescript/serdes/serdes.ts @@ -0,0 +1,86 @@ +export type PrimitiveType = number | string | boolean +export type JsonArray = TValue[] +export type JsonTableValueType = + PrimitiveType + | JsonTable + | JsonArray +export type JsonTable = LuaTable // & table +// export type JsonArray = LuaArray // & table + + +export namespace Serdes { + + export namespace Player { + + export function playerToTable(player: LuaPlayer): JsonTable { + let table = new LuaTable() + + table.set("name", player.name) + let charIds: JsonArray = Entity.entitiesToUnitNumbers(player.get_associated_characters()) + table.set("characterIds", charIds) + + return table + } + + } + + export namespace Entity { + + export function entityToTable(entity: LuaEntity): JsonTable { + let table = new LuaTable() + + if (entity.player?.index != null) { + table.set("playerId", entity.player?.index) + } + table.set("type", entity.type) + table.set("active", entity.active) + + if (entity.health != null) { + table.set("health", entity.health) + } + + table.set("surfaceId", entity.surface.index) + table.set("position", DataClasses.positionTableToTable(entity.position)) + + return table + } + + export function entitiesToUnitNumbers(entities: LuaEntity[]): JsonArray { + let array: number[] = [] + let j: number = 0 + for (let i = 0; i < entities.length; i++) { + let unitNum = entities[i].unit_number + if (unitNum != null) { + array[j++] = unitNum + } + } + return array + } + + } + + export namespace Surface { + + export function surfaceToTable(surface: LuaSurface): JsonTable { + let table = new LuaTable() + + table.set("name", surface.name) + table.set("index", surface.index) + table.set("daytime", surface.daytime) + + return table + } + + } + + export namespace DataClasses { + + export function positionTableToTable(positionTable: PositionTable): JsonTable { + let table = new LuaTable() + table.set("x", positionTable.x) + table.set("y", positionTable.y) + return table + } + + } +} diff --git a/modules/mod/src/main/typescript/tsconfig.json b/modules/mod/src/main/typescript/tsconfig.json index a6fa1f8..dbd7f66 100644 --- a/modules/mod/src/main/typescript/tsconfig.json +++ b/modules/mod/src/main/typescript/tsconfig.json @@ -22,12 +22,13 @@ "strict": true, "strictFunctionTypes": true, "strictNullChecks": true, - "traceResolution": true, + "traceResolution": true }, "tstl": { "luaTarget": "5.2", "tstlVerbose": true, "noHeader": true, + "noImplicitSelf": true, "luaLibImport" : "require", }, "//": { diff --git a/modules/mod2/build.gradle.kts b/modules/mod2/build.gradle.kts index 60d7616..586e5cb 100644 --- a/modules/mod2/build.gradle.kts +++ b/modules/mod2/build.gradle.kts @@ -62,12 +62,9 @@ kotlin { } dependencies { - val kotlinWrappersVersion = "0.0.1-pre.254-kotlin-1.5.31" - implementation( - project.dependencies.enforcedPlatform( - "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${kotlinWrappersVersion}" - ) - ) + + + // bom is defined in archetype.kotlin-js implementation("org.jetbrains.kotlin-wrappers:kotlin-react") // implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom")