- make each event update have a key/data interface

- separate .d.ts file for event updates
-  fix initialisation of global items
This commit is contained in:
Adam 2022-02-23 11:51:15 +01:00
parent 494913d3b9
commit 5208d9affd
14 changed files with 451 additions and 266 deletions

View file

@ -1,16 +1,17 @@
import {emitConfigurationUpdate} from "./main/config-update/handleConfigurationUpdate"; import {emitConfigurationUpdate} from "./main/config-update/handleConfigurationUpdate";
import {emitPrototypes} from "./main/config-update/prototypes"; import {emitPrototypes} from "./main/config-update/prototypes";
import {Queue} from "./main/queue/queue"; import {initGlobal} from "./main/global-init";
import {EventDataCache} from "./main/cache/EventDataCache";
require("./main/events/registerHandlers") require("./main/events/registerHandlers")
require("./main/events/playerEvents")
require("./main/events/mapEvents")
require("./main/commands/kafkatorioCommands") require("./main/commands/kafkatorioCommands")
require("./main/cache/cacheChecker") require("./main/cache/cacheChecker")
script.on_configuration_changed((data: ConfigurationChangedData) => { script.on_configuration_changed((data: ConfigurationChangedData) => {
// runs whenever the version of the base game, or a mod, changes // runs whenever the version of the base game, or a mod, changes
reInitGlobal() initGlobal(true)
emitConfigurationUpdate(data) emitConfigurationUpdate(data)
emitPrototypes() emitPrototypes()
}) })
@ -18,27 +19,5 @@ script.on_configuration_changed((data: ConfigurationChangedData) => {
script.on_init(() => { script.on_init(() => {
// runs only once (when the mod is first added to the save) // runs only once (when the mod is first added to the save)
initGlobal() initGlobal(true)
}) })
function initGlobal() {
log("Initialising Kafkatorio Global variables...")
Queue.init()
EventDataCache.init()
log("Finished initialising Kafkatorio Global variables")
}
function reInitGlobal() {
log("Re-initialising Kafkatorio Global variables...")
global.MOD_VERSION = script.active_mods[script.mod_name]
global.FACTORIO_VERSION = script.active_mods["base"]
Queue.reset()
EventDataCache.reset()
log("Finished re-initialising Kafkatorio Global variables")
}

View file

@ -6,6 +6,33 @@ interface FactorioObjectData {
objectName: ObjectName; objectName: ObjectName;
} }
interface PlayerData extends FactorioObjectData, FactorioLuaControl {
associatedCharactersUnitNumbers: uint[];
characterUnitNumber: uint | null;
chatColour: Colour;
colour: Colour;
force: uint;
lastOnline: uint;
name: string;
objectName: ObjectName;
position: MapEntityPosition;
surface: uint;
}
interface EntityData extends FactorioObjectData, FactorioLuaControl {
active: boolean;
force: uint;
health: double | null;
healthRatio: double;
name: string;
objectName: ObjectName;
playerIndex: uint | null;
position: MapEntityPosition;
surface: uint;
type: string;
unitNumber: uint | null;
}
interface SurfaceData extends FactorioObjectData { interface SurfaceData extends FactorioObjectData {
daytime: double; daytime: double;
index: uint; index: uint;

View file

@ -7,19 +7,6 @@ interface KafkatorioPacket {
packetType: PacketType; packetType: PacketType;
} }
type FactorioEventUpdateType = "PLAYER" | "MAP_CHUNK" | "ENTITY";
interface FactorioEventUpdate {
updateType: FactorioEventUpdateType;
}
interface FactorioEventUpdatePacket extends KafkatorioPacket {
modVersion: string;
packetType: PacketType;
tick: uint;
update: FactorioEventUpdate;
}
interface FactorioEvent extends KafkatorioPacket { interface FactorioEvent extends KafkatorioPacket {
data: FactorioObjectData; data: FactorioObjectData;
eventType: string; eventType: string;
@ -46,33 +33,6 @@ interface Colour {
red: float; red: float;
} }
interface PlayerData extends FactorioObjectData, FactorioLuaControl {
associatedCharactersUnitNumbers: uint[];
characterUnitNumber: uint | null;
chatColour: Colour;
colour: Colour;
force: uint;
lastOnline: uint;
name: string;
objectName: ObjectName;
position: MapEntityPosition;
surface: uint;
}
interface EntityData extends FactorioObjectData, FactorioLuaControl {
active: boolean;
force: uint;
health: double | null;
healthRatio: double;
name: string;
objectName: ObjectName;
playerIndex: uint | null;
position: MapEntityPosition;
surface: uint;
type: string;
unitNumber: uint | null;
}
interface MapTilePosition { interface MapTilePosition {
x: int; x: int;
y: int; y: int;
@ -99,56 +59,3 @@ interface EntityIdentifiers {
type: string; type: string;
unitNumber: uint | null; unitNumber: uint | null;
} }
interface PlayerUpdate extends FactorioEventUpdate {
afkTime: uint | null;
bannedReason: string | null;
characterUnitNumber: uint | null;
chatColour: Colour | null;
colour: Colour | null;
diedCause: EntityIdentifiers | null;
disconnectReason: string | null;
forceIndex: uint | null;
index: uint;
isAdmin: boolean | null;
isConnected: boolean | null;
isRemoved: boolean | null;
isShowOnMap: boolean | null;
isSpectator: boolean | null;
kickedReason: string | null;
lastOnline: uint | null;
name: string | null;
onlineTime: uint | null;
position: MapEntityPosition | null;
surfaceIndex: uint | null;
tag: string | null;
ticksToRespawn: uint | null;
updateType: FactorioEventUpdateType;
}
interface EntityUpdate extends FactorioEventUpdate, EntityIdentifiers {
chunkPosition: MapChunkPosition | null;
graphicsVariation: number | null;
health: float | null;
isActive: boolean | null;
isRotatable: boolean | null;
lastUser: int | null;
localisedDescription: string | null;
localisedName: string | null;
name: string;
prototype: string | null;
type: string;
unitNumber: uint;
updateType: FactorioEventUpdateType;
}
interface MapChunkUpdate extends FactorioEventUpdate {
chunkPosition: MapChunkPosition;
force: uint | null;
isDeleted: boolean | null;
player: uint | null;
robot: EntityIdentifiers | null;
surfaceIndex: uint;
tiles: Map<MapTilePosition, string> | null;
updateType: FactorioEventUpdateType;
}

View file

@ -0,0 +1,134 @@
// Generated by TypeScriptGenerator - do not edit this file manually
type FactorioEventUpdateType = "PLAYER" | "MAP_CHUNK" | "ENTITY";
interface FactorioEventUpdateKey {
updateType: FactorioEventUpdateType;
}
interface FactorioEventUpdateData {
updateType: FactorioEventUpdateType;
}
interface FactorioEventUpdate extends FactorioEventUpdateKey, FactorioEventUpdateData {
updateType: FactorioEventUpdateType;
}
interface FactorioEventUpdatePacket extends KafkatorioPacket {
modVersion: string;
packetType: PacketType;
tick: uint;
update: FactorioEventUpdate;
}
interface PlayerUpdateKey extends FactorioEventUpdateKey {
index: uint;
}
interface PlayerUpdateData extends FactorioEventUpdateData {
afkTime: uint | null;
bannedReason: string | null;
characterUnitNumber: uint | null;
chatColour: Colour | null;
colour: Colour | null;
diedCause: EntityIdentifiers | null;
disconnectReason: string | null;
forceIndex: uint | null;
isAdmin: boolean | null;
isConnected: boolean | null;
isRemoved: boolean | null;
isShowOnMap: boolean | null;
isSpectator: boolean | null;
kickedReason: string | null;
lastOnline: uint | null;
name: string | null;
onlineTime: uint | null;
position: MapEntityPosition | null;
surfaceIndex: uint | null;
tag: string | null;
ticksToRespawn: uint | null;
}
interface PlayerUpdate extends FactorioEventUpdate, PlayerUpdateKey, PlayerUpdateData {
afkTime: uint | null;
bannedReason: string | null;
characterUnitNumber: uint | null;
chatColour: Colour | null;
colour: Colour | null;
diedCause: EntityIdentifiers | null;
disconnectReason: string | null;
forceIndex: uint | null;
index: uint;
isAdmin: boolean | null;
isConnected: boolean | null;
isRemoved: boolean | null;
isShowOnMap: boolean | null;
isSpectator: boolean | null;
kickedReason: string | null;
lastOnline: uint | null;
name: string | null;
onlineTime: uint | null;
position: MapEntityPosition | null;
surfaceIndex: uint | null;
tag: string | null;
ticksToRespawn: uint | null;
updateType: FactorioEventUpdateType;
}
interface EntityUpdateKey extends EntityIdentifiers, FactorioEventUpdateKey {
name: string;
type: string;
unitNumber: uint;
}
interface EntityUpdateData extends FactorioEventUpdateData {
chunkPosition: MapChunkPosition | null;
graphicsVariation: number | null;
health: float | null;
isActive: boolean | null;
isRotatable: boolean | null;
lastUser: int | null;
localisedDescription: string | null;
localisedName: string | null;
prototype: string | null;
}
interface EntityUpdate extends FactorioEventUpdate, EntityIdentifiers, EntityUpdateKey, EntityUpdateData {
chunkPosition: MapChunkPosition | null;
graphicsVariation: number | null;
health: float | null;
isActive: boolean | null;
isRotatable: boolean | null;
lastUser: int | null;
localisedDescription: string | null;
localisedName: string | null;
name: string;
prototype: string | null;
type: string;
unitNumber: uint;
updateType: FactorioEventUpdateType;
}
interface MapChunkUpdateKey extends FactorioEventUpdateKey {
chunkPosition: MapChunkPosition;
surfaceIndex: uint;
}
interface MapChunkUpdateData extends FactorioEventUpdateData {
force: uint | null;
isDeleted: boolean | null;
player: uint | null;
robot: EntityIdentifiers | null;
tiles: MapTile[] | null;
}
interface MapChunkUpdate extends FactorioEventUpdate, MapChunkUpdateKey, MapChunkUpdateData {
chunkPosition: MapChunkPosition;
force: uint | null;
isDeleted: boolean | null;
player: uint | null;
robot: EntityIdentifiers | null;
surfaceIndex: uint;
tiles: MapTile[] | null;
updateType: FactorioEventUpdateType;
}

View file

@ -1,32 +1,34 @@
import {ExcludeNullKeys, NonNullableKeys} from "../types"; import {NonNullableKeys} from "../types";
export namespace EventDataCache { export namespace EventDataCache {
declare const global: { declare const global: {
cache: LuaTable<CacheKey<FactorioEventUpdateType>, CacheEntry<FactorioEventUpdateType>>, cache: LuaTable<CacheKey<FactorioEventUpdateType>, CacheEntry<FactorioEventUpdateType>>,
DEFAULT_CACHE_DURATION_TICKS2: Record<FactorioEventUpdateType | "default", uint>, defaultCacheDurationTicks: Record<FactorioEventUpdateType | "default", uint>,
} }
export function reset() { export function init(force?: boolean) {
log("Resetting EventDataCache")
global.cache = new LuaTable<CacheKey<FactorioEventUpdateType>, CacheEntry<FactorioEventUpdateType>>()
}
const isAnythingUndefined = global.cache == undefined ||
global.defaultCacheDurationTicks == undefined
export function init() { log(`Initialising EventDataCache (force=${force}, isAnythingUndefined=${isAnythingUndefined})...`)
if (typeof global.cache == undefined) {
log("Initialising EventDataCache")
reset()
global.DEFAULT_CACHE_DURATION_TICKS2 = { if (force == true || isAnythingUndefined) {
"PLAYER": 30, // 0.5 seconds log("Initialising global.cache")
"MAP_CHUNK": 60 * 15, // ~ 15 seconds global.cache = new LuaTable<CacheKey<FactorioEventUpdateType>, CacheEntry<FactorioEventUpdateType>>()
"ENTITY": 60 * 2, // ~2 seconds
"default": 60 log("Initialising global.DEFAULT_CACHE_DURATION_TICKS")
global.defaultCacheDurationTicks = {
"PLAYER": 60, // 1 second
"MAP_CHUNK": 60 * 30, // 30 seconds
"ENTITY": 60 * 2, // 2 seconds
"default": 60 * 5, // 5 seconds
} }
} }
log(`Finished initialising EventDataCache`)
} }
@ -73,7 +75,7 @@ export namespace EventDataCache {
*/ */
export function setExpiration( export function setExpiration(
key: CacheKey<FactorioEventUpdateType>, key: CacheKey<FactorioEventUpdateType>,
expirationDurationTicks?: uint, expirationDurationTicks: uint,
) { ) {
const entry = getCacheEntry(key) const entry = getCacheEntry(key)
if (entry != undefined) { if (entry != undefined) {
@ -82,11 +84,12 @@ export namespace EventDataCache {
} }
export function extractExpired(): Array<CacheData<FactorioEventUpdateType>> { export function extractExpired(): Array<FactorioEventUpdate> {
const expiredData: Array<CacheData<FactorioEventUpdateType>> = [] const expiredData: Array<FactorioEventUpdate> = []
for (let [key, entry] of global.cache) { for (let [key, entry] of global.cache) {
if (entry.isExpired()) { if (isExpired(entry)) {
expiredData.push(entry.data) const update: FactorioEventUpdate = {...key, ...entry.data}
expiredData.push(update)
global.cache.delete(key) global.cache.delete(key)
} }
} }
@ -114,7 +117,7 @@ export namespace EventDataCache {
key: CacheKey<TYPE>, key: CacheKey<TYPE>,
mutate: CacheDataMutator<TYPE>, mutate: CacheDataMutator<TYPE>,
): CacheEntry<TYPE> { ): CacheEntry<TYPE> {
// @ts-ignore // TODO remove ts-ignore for "not assignable to type" // @ts-ignore // TODO resolve ts-ignore for "not assignable to type"
const data: CacheData<TYPE> = {updateType: key.updateType} const data: CacheData<TYPE> = {updateType: key.updateType}
mutate(data) mutate(data)
let entry: CacheEntry<TYPE> = new CacheEntry<TYPE>(data) let entry: CacheEntry<TYPE> = new CacheEntry<TYPE>(data)
@ -123,24 +126,35 @@ export namespace EventDataCache {
} }
/** Map a {@link FactorioEventUpdateType} to a {@link FactorioEventUpdate} DTO */ // /** Map a {@link FactorioEventUpdateType} to a {@link FactorioEventUpdate} DTO */
type ConvertUpdateType<TYPE extends FactorioEventUpdateType> = // type ConvertUpdateType<TYPE extends FactorioEventUpdateType> =
TYPE extends "PLAYER" ? PlayerUpdate : // TYPE extends "PLAYER" ? PlayerUpdate :
TYPE extends "MAP_CHUNK" ? MapChunkUpdate : // TYPE extends "MAP_CHUNK" ? MapChunkUpdate :
TYPE extends "ENTITY" ? EntityUpdate : // TYPE extends "ENTITY" ? EntityUpdate :
// never
type ConvertKeyType<TYPE extends FactorioEventUpdateType> =
TYPE extends "PLAYER" ? PlayerUpdateKey :
TYPE extends "MAP_CHUNK" ? MapChunkUpdateKey :
TYPE extends "ENTITY" ? EntityUpdateKey :
never
type ConvertKeyData<TYPE extends FactorioEventUpdateType> =
TYPE extends "PLAYER" ? PlayerUpdateData :
TYPE extends "MAP_CHUNK" ? MapChunkUpdateData :
TYPE extends "ENTITY" ? EntityUpdateData :
never never
/** Only include the non-null properties, and make `updateType` specific */ /** Only include the non-null properties, and make `updateType` specific */
export type CacheKey<TYPE extends FactorioEventUpdateType> = export type CacheKey<TYPE extends FactorioEventUpdateType> =
CacheTyped<TYPE> CacheTyped<TYPE>
& Omit<ExcludeNullKeys<ConvertUpdateType<TYPE>>, "updateType"> & Omit<ConvertKeyType<TYPE>, "updateType">
/** Exclude {@link CacheKey} fields, and make `updateType` specific */ /** Exclude {@link CacheKey} fields, and make `updateType` specific */
export type CacheData<TYPE extends FactorioEventUpdateType> = export type CacheData<TYPE extends FactorioEventUpdateType> =
CacheTyped<TYPE> CacheTyped<TYPE>
& NonNullableKeys<Partial<Omit<ConvertUpdateType<TYPE>, keyof CacheKey<TYPE> | "updateType">>> & NonNullableKeys<Partial<Omit<ConvertKeyData<TYPE>, "updateType">>>
export type CacheTyped<TYPE extends FactorioEventUpdateType> = { export type CacheTyped<TYPE extends FactorioEventUpdateType> = {
@ -163,17 +177,15 @@ export namespace EventDataCache {
expirationDurationTicks?: uint, expirationDurationTicks?: uint,
) { ) {
this.data = data this.data = data
if (expirationDurationTicks != undefined) { this.expirationDurationTicks = expirationDurationTicks
this.expirationDurationTicks = expirationDurationTicks
}
} }
}
isExpired(): boolean {
const cacheDuration =
this.expirationDurationTicks ?? global.DEFAULT_CACHE_DURATION_TICKS2[this.data.updateType]
return game.tick - this.lastUpdatedTick > cacheDuration function isExpired<TYPE extends FactorioEventUpdateType>(entry: CacheEntry<TYPE>): boolean {
} const expiryDuration =
entry.expirationDurationTicks ?? global.defaultCacheDurationTicks[entry.data.updateType]
return game.tick - entry.lastUpdatedTick > expiryDuration
} }

View file

@ -4,7 +4,7 @@ import {emitPacket} from "../emitKafkatorioPacket";
/** Every 0.1 seconds */ /** Every 0.1 seconds */
script.on_nth_tick(6, (nthTick: NthTickEventData) => { script.on_nth_tick(6, (nthTick: NthTickEventData) => {
const cachedEvents: Array<EventDataCache.CacheData<any>> = EventDataCache.extractExpired() const cachedEvents: Array<FactorioEventUpdate> = EventDataCache.extractExpired()
if (cachedEvents.length > 0) { if (cachedEvents.length > 0) {
log(`nth tick ${nthTick.tick} has ${cachedEvents.length} events`) log(`nth tick ${nthTick.tick} has ${cachedEvents.length} events`)
@ -22,34 +22,3 @@ script.on_nth_tick(6, (nthTick: NthTickEventData) => {
} }
} }
}) })
//
// function createEntityUpdate(cacheKey: EntityCacheKey, events: EventName[]): EntityUpdate | null {
//
//
// return null
// }
//
// interface EntityUpdate {
// name: string
//
// unit_number: uint
//
// unhandledEvents?: string[]
//
// chunkPosition: MapChunkPosition
// graphics_variation?: uint8
// health?: float
// isActive?: boolean
// isRotatable?: boolean
// last_user?: uint
// localised_description?: string
// localised_name?: string
// prototype?: string
//
// // ghost_localised_description?: string
// // ghost_localised_name?: string
// // ghost_name?: string
// // ghost_prototype?: string
// // ghost_unit_number?: uint
// }

View file

@ -1,5 +1,6 @@
import {emitPrototypes} from "../config-update/prototypes"; import {emitPrototypes} from "../config-update/prototypes";
import {Queue} from "../queue/queue"; import {Queue} from "../queue/queue";
import {initGlobal} from "../global-init";
import floor = math.floor; import floor = math.floor;
@ -8,18 +9,20 @@ commands.add_command(
"kafkatorio innit bruv", "kafkatorio innit bruv",
(e: CustomCommandData) => { (e: CustomCommandData) => {
let player = (e.player_index != null) ? game.get_player(e.player_index) : null const player = (e.player_index != null) ? game.get_player(e.player_index) : null
if (e.parameter == undefined) { const paramUppercase = e.parameter?.toUpperCase()
if (paramUppercase == undefined) {
if (player != null) { if (player != null) {
player.print("no parameter") player.print("no parameter")
} }
// do nothing // do nothing
} else if ("PROTOTYPES" == e.parameter.toUpperCase()) { } else if ("PROTOTYPES" == paramUppercase) {
emitPrototypes() emitPrototypes()
} else if (e.parameter.toUpperCase().startsWith("CHUNKS")) { } else if (paramUppercase.startsWith("CHUNKS")) {
let size = e.parameter.split(" ")?.[1] ?? null let size = paramUppercase.split(" ")?.[1] ?? null
let radius = (size != null) ? parseInt(size) : 1 let radius = (size != null) ? parseInt(size) : 1
if (e.player_index != undefined) { if (e.player_index != undefined) {
@ -60,10 +63,8 @@ commands.add_command(
} }
} }
} }
} else if ("QUEUE_RESET" == e.parameter.toUpperCase()) { } else if (paramUppercase.startsWith("INIT_GLOBAL")) {
Queue.reset() initGlobal(paramUppercase.startsWith("INIT_GLOBAL FORCE"))
} else if ("QUEUE_INIT" == e.parameter.toUpperCase()) {
Queue.init()
} }
} }
) )

View file

@ -1,5 +1,5 @@
function isEventType<ED extends EventData | CustomInputEvent>( export function isEventType<ED extends EventData | CustomInputEvent>(
eventData: EventData, eventData: EventData,
type: ED extends CustomInputEvent ? string : EventId<ED> type: ED extends CustomInputEvent ? string : EventId<ED>
): eventData is ED { ): eventData is ED {

View file

@ -1,4 +1,5 @@
import {EventDataCache} from "../cache/EventDataCache"; import {EventDataCache} from "../cache/EventDataCache";
import {isEventType} from "./eventTypeCheck";
import CacheKey = EventDataCache.CacheKey; import CacheKey = EventDataCache.CacheKey;
import CacheData = EventDataCache.CacheData; import CacheData = EventDataCache.CacheData;
@ -24,12 +25,17 @@ function mapTilesUpdateDebounce(
}, },
data => { data => {
if (data.tiles == undefined) { if (data.tiles == undefined) {
data.tiles = new Map<MapTilePosition, string>() data.tiles = []
} }
for (const tile of tiles) { for (const tile of tiles) {
const position: MapTilePosition = {x: tile.position.x, y: tile.position.y} data.tiles.push(
data.tiles.set(position, tile.name) {
x: tile.position.x,
y: tile.position.y,
proto: tile.name,
}
)
} }
if (updater != undefined) { if (updater != undefined) {
@ -58,6 +64,8 @@ function getSurface(surfaceIndex: uint): LuaSurface | undefined {
script.on_event( script.on_event(
defines.events.on_chunk_generated, defines.events.on_chunk_generated,
(e: OnChunkGeneratedEvent) => { (e: OnChunkGeneratedEvent) => {
log(`on_chunk_generated ${e.tick}`)
let tiles = getTiles(e.surface, e.area) let tiles = getTiles(e.surface, e.area)
mapTilesUpdateDebounce(e.surface, e.position, tiles) mapTilesUpdateDebounce(e.surface, e.position, tiles)
} }
@ -66,6 +74,7 @@ script.on_event(
script.on_event( script.on_event(
defines.events.on_chunk_charted, defines.events.on_chunk_charted,
(e: OnChunkChartedEvent) => { (e: OnChunkChartedEvent) => {
log(`on_chunk_charted ${e.tick}`)
const surface = getSurface(e.surface_index) const surface = getSurface(e.surface_index)
if (surface == undefined) { if (surface == undefined) {
@ -82,6 +91,7 @@ script.on_event(
script.on_event( script.on_event(
defines.events.script_raised_set_tiles, defines.events.script_raised_set_tiles,
(e: ScriptRaisedSetTilesEvent) => { (e: ScriptRaisedSetTilesEvent) => {
log(`script_raised_set_tiles ${e.tick}`)
const surface = getSurface(e.surface_index) const surface = getSurface(e.surface_index)
if (surface == undefined) { if (surface == undefined) {
@ -164,8 +174,14 @@ function onBuildTileEvent(event: OnPlayerBuiltTileEvent | OnRobotBuiltTileEvent)
} }
} }
script.on_event(defines.events.on_player_built_tile, (e: OnPlayerBuiltTileEvent) => onBuildTileEvent(e)) script.on_event(defines.events.on_player_built_tile, (e: OnPlayerBuiltTileEvent) => {
script.on_event(defines.events.on_robot_built_tile, (e: OnRobotBuiltTileEvent) => onBuildTileEvent(e)) log(`on_player_built_tile ${e.tick}`)
onBuildTileEvent(e)
})
script.on_event(defines.events.on_robot_built_tile, (e: OnRobotBuiltTileEvent) => {
log(`on_robot_built_tile ${e.tick}`)
onBuildTileEvent(e)
})
// script.on_event( // script.on_event(
// defines.events.on_player_mined_tile, // defines.events.on_player_mined_tile,
@ -187,6 +203,7 @@ script.on_event(defines.events.on_robot_built_tile, (e: OnRobotBuiltTileEvent) =
script.on_event( script.on_event(
defines.events.on_pre_chunk_deleted, defines.events.on_pre_chunk_deleted,
(e: OnPreChunkDeletedEvent) => { (e: OnPreChunkDeletedEvent) => {
log(`on_pre_chunk_deleted ${e.tick}`)
const surface = getSurface(e.surface_index) const surface = getSurface(e.surface_index)
if (surface == undefined) { if (surface == undefined) {

View file

@ -7,6 +7,7 @@ import {
} from "./handlers"; } from "./handlers";
import {Queue} from "../queue/queue"; import {Queue} from "../queue/queue";
import {EventName} from "../types"; import {EventName} from "../types";
import {isEventType} from "./eventTypeCheck";
const mapEventIdToName = new LuaTable<defines.Events, EventName>() const mapEventIdToName = new LuaTable<defines.Events, EventName>()

View file

@ -0,0 +1,15 @@
import {Queue} from "./queue/queue";
import {EventDataCache} from "./cache/EventDataCache";
export function initGlobal(force: boolean = false) {
log(`Initialising Kafkatorio Global variables (force=${force})...`)
global.MOD_VERSION = script.active_mods[script.mod_name]
global.FACTORIO_VERSION = script.active_mods["base"]
Queue.init(true)
EventDataCache.init(true)
log("Finished initialising Kafkatorio Global variables")
}

View file

@ -10,24 +10,27 @@ export namespace Queue {
} }
export function init() { export function init(force?: boolean) {
if (global.store == undefined) {
log("initialising EventQueue") const isAnythingUndefined = global.store == undefined ||
reset() global.size == undefined ||
global.head == undefined ||
global.tail == undefined
log(`Initialising EventQueue globals (force=${force}, isAnythingUndefined=${isAnythingUndefined})`)
if (force == true || isAnythingUndefined) {
global.store = {}
global.head = null
global.tail = null
global.size = 0
} }
log(`Finished initialising EventQueue`)
} }
export function reset() { export function size(): uint | undefined {
log("Resetting EventQueue")
global.store = {}
global.head = null
global.tail = null
global.size = 0
}
export function size(): uint {
return global.size return global.size
} }

View file

@ -1,5 +1,6 @@
package dev.adamko.kafkatorio.events.schema package dev.adamko.kafkatorio.events.schema
import dev.adamko.kafkatorio.events.schema.FactorioEventUpdate.FactorioEventUpdateType
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.EncodeDefault import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -22,11 +23,24 @@ class FactorioEventUpdatePacket(
} }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
sealed interface FactorioEventUpdateKey {
val updateType: FactorioEventUpdateType
}
sealed interface FactorioEventUpdateData {
val updateType: FactorioEventUpdateType
}
@Serializable(with = FactorioEventUpdate.Companion.JsonSerializer::class) @Serializable(with = FactorioEventUpdate.Companion.JsonSerializer::class)
sealed class FactorioEventUpdate { sealed class FactorioEventUpdate : FactorioEventUpdateKey, FactorioEventUpdateData {
@EncodeDefault @EncodeDefault
abstract val updateType: FactorioEventUpdateType abstract override val updateType: FactorioEventUpdateType
@Serializable @Serializable
enum class FactorioEventUpdateType { enum class FactorioEventUpdateType {
@ -61,7 +75,7 @@ sealed class FactorioEventUpdate {
return when (type) { return when (type) {
FactorioEventUpdateType.PLAYER -> PlayerUpdate.serializer() FactorioEventUpdateType.PLAYER -> PlayerUpdate.serializer()
FactorioEventUpdateType.MAP_CHUNK -> TODO() FactorioEventUpdateType.MAP_CHUNK -> MapChunkUpdate.serializer()
FactorioEventUpdateType.ENTITY -> EntityUpdate.serializer() FactorioEventUpdateType.ENTITY -> EntityUpdate.serializer()
} }
} }
@ -71,75 +85,154 @@ sealed class FactorioEventUpdate {
} }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
sealed interface PlayerUpdateKey : FactorioEventUpdateKey {
val index: PlayerIndex
}
sealed interface PlayerUpdateData : FactorioEventUpdateData {
val characterUnitNumber: UnitNumber?
val chatColour: Colour?
val colour: Colour?
val name: String?
val afkTime: Tick?
val ticksToRespawn: Tick?
val forceIndex: ForceIndex?
val isAdmin: Boolean?
val isConnected: Boolean?
val isShowOnMap: Boolean?
val isSpectator: Boolean?
val lastOnline: Tick?
val onlineTime: Tick?
val position: MapEntityPosition?
val surfaceIndex: SurfaceIndex?
val tag: String?
val diedCause: EntityIdentifiers?
val bannedReason: String?
val kickedReason: String?
val disconnectReason: String?
/** `true` when a player is removed (deleted) from the game */
val isRemoved: Boolean?
}
@Serializable @Serializable
data class PlayerUpdate( data class PlayerUpdate(
val index: PlayerIndex, override val index: PlayerIndex,
val characterUnitNumber: UnitNumber? = null, override val characterUnitNumber: UnitNumber? = null,
val chatColour: Colour? = null, override val chatColour: Colour? = null,
val colour: Colour? = null, override val colour: Colour? = null,
val name: String? = null, override val name: String? = null,
val afkTime: Tick? = null, override val afkTime: Tick? = null,
val ticksToRespawn: Tick? = null, override val ticksToRespawn: Tick? = null,
val forceIndex: ForceIndex? = null, override val forceIndex: ForceIndex? = null,
val isAdmin: Boolean? = null, override val isAdmin: Boolean? = null,
val isConnected: Boolean? = null, override val isConnected: Boolean? = null,
val isShowOnMap: Boolean? = null, override val isShowOnMap: Boolean? = null,
val isSpectator: Boolean? = null, override val isSpectator: Boolean? = null,
val lastOnline: Tick? = null, override val lastOnline: Tick? = null,
val onlineTime: Tick? = null, override val onlineTime: Tick? = null,
val position: MapEntityPosition? = null, override val position: MapEntityPosition? = null,
val surfaceIndex: SurfaceIndex? = null, override val surfaceIndex: SurfaceIndex? = null,
val tag: String? = null, override val tag: String? = null,
val diedCause: EntityIdentifiers? = null, override val diedCause: EntityIdentifiers? = null,
val bannedReason: String? = null, override val bannedReason: String? = null,
val kickedReason: String? = null, override val kickedReason: String? = null,
val disconnectReason: String? = null, override val disconnectReason: String? = null,
/** `true` when a player is removed (deleted) from the game */ /** `true` when a player is removed (deleted) from the game */
val isRemoved: Boolean? = null, override val isRemoved: Boolean? = null,
) : FactorioEventUpdate() { ) : FactorioEventUpdate(), PlayerUpdateKey, PlayerUpdateData {
@EncodeDefault @EncodeDefault
override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.PLAYER override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.PLAYER
} }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
sealed interface EntityUpdateKey : EntityIdentifiers, FactorioEventUpdateKey {
/** A [unitNumber] is required for caching */
override val unitNumber: UnitNumber
override val name: String
override val type: String
}
sealed interface EntityUpdateData : FactorioEventUpdateData {
val chunkPosition: MapChunkPosition?
val graphicsVariation: UShort?
val health: Float?
val isActive: Boolean?
val isRotatable: Boolean?
val lastUser: UInt?
val localisedDescription: String?
val localisedName: String?
val prototype: PrototypeName?
}
@Serializable @Serializable
data class EntityUpdate( data class EntityUpdate(
/** A [unitNumber] is required for caching */
override val unitNumber: UnitNumber, override val unitNumber: UnitNumber,
override val name: String, override val name: String,
override val type: String, override val type: String,
val chunkPosition: MapChunkPosition? = null, override val chunkPosition: MapChunkPosition? = null,
val graphicsVariation: UShort? = null, override val graphicsVariation: UShort? = null,
val health: Float? = null, override val health: Float? = null,
val isActive: Boolean? = null, override val isActive: Boolean? = null,
val isRotatable: Boolean? = null, override val isRotatable: Boolean? = null,
val lastUser: UInt? = null, override val lastUser: UInt? = null,
val localisedDescription: String? = null, override val localisedDescription: String? = null,
val localisedName: String? = null, override val localisedName: String? = null,
val prototype: PrototypeName? = null, override val prototype: PrototypeName? = null,
) : FactorioEventUpdate(), EntityIdentifiers { ) : FactorioEventUpdate(), EntityIdentifiers, EntityUpdateKey, EntityUpdateData {
@EncodeDefault @EncodeDefault
override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.ENTITY override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.ENTITY
} }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
sealed interface MapChunkUpdateKey : FactorioEventUpdateKey {
val chunkPosition: MapChunkPosition
val surfaceIndex: SurfaceIndex
}
sealed interface MapChunkUpdateData : FactorioEventUpdateData {
val player: PlayerIndex?
val robot: EntityIdentifiers?
val force: ForceIndex?
/** updated tiles - might be partial */
val tiles: List<MapTile>?
val isDeleted: Boolean?
}
@Serializable @Serializable
data class MapChunkUpdate( data class MapChunkUpdate(
val chunkPosition: MapChunkPosition, override val chunkPosition: MapChunkPosition,
val surfaceIndex: SurfaceIndex, override val surfaceIndex: SurfaceIndex,
val player: PlayerIndex? = null, override val player: PlayerIndex? = null,
val robot: EntityIdentifiers? = null, override val robot: EntityIdentifiers? = null,
val force: ForceIndex? = null, override val force: ForceIndex? = null,
// note: this must be a List, not a Map<Location, ProtoName>
/** Might be a partial list of updated tiles */ // because JSON can't have non-string keys.
val tiles: Map<MapTilePosition, PrototypeName>? = null, override val tiles: List<MapTile>? = null,
val isDeleted: Boolean? = null, override val isDeleted: Boolean? = null,
) : FactorioEventUpdate() { ) : FactorioEventUpdate(), MapChunkUpdateKey, MapChunkUpdateData {
@EncodeDefault @EncodeDefault
override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.MAP_CHUNK override val updateType: FactorioEventUpdateType = FactorioEventUpdateType.MAP_CHUNK
} }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

View file

@ -14,12 +14,19 @@ fun main(args: Array<String>) {
TypeScriptGenerator( TypeScriptGenerator(
rootClasses = buildSet { rootClasses = buildSet {
addAll(KafkatorioPacket::class.sealedSubclasses) addAll(KafkatorioPacket::class.sealedSubclasses)
addAll(FactorioObjectData::class.sealedSubclasses) addAll(FactorioObjectData::class.sealedSubclasses)
addAll(FactorioPrototype::class.sealedSubclasses) addAll(FactorioPrototype::class.sealedSubclasses)
add(FactorioPrototypes::class) add(FactorioPrototypes::class)
add(FactorioConfigurationUpdate::class) add(FactorioConfigurationUpdate::class)
add(FactorioEventUpdatePacket::class) add(FactorioEventUpdatePacket::class)
addAll(FactorioEventUpdate::class.sealedSubclasses) addAll(FactorioEventUpdate::class.sealedSubclasses)
addAll(FactorioEventUpdateKey::class.sealedSubclasses)
addAll(FactorioEventUpdateData::class.sealedSubclasses)
}, },
mappings = mapOf( mappings = mapOf(
// builtin Factorio numeric types > `typed-factorio/generated/builtin-types.d.ts` // builtin Factorio numeric types > `typed-factorio/generated/builtin-types.d.ts`
@ -66,18 +73,38 @@ private fun splitDefinitions(definitions: String): Map<String, String> {
return definitions return definitions
.split("\n\n") .split("\n\n")
.groupBy { def -> .groupBy { def ->
when { when {
"extends FactorioObjectData " in def ||
"interface FactorioObjectData " in def || "interface FactorioObjectData " in def ||
"type ObjectName " in def -> "object-data.d.ts" "type ObjectName " in def ||
def.contains(Regex("interface.+extends.+(FactorioObjectData).*"))
-> "object-data.d.ts"
"extends FactorioPrototype " in def || "extends FactorioPrototype " in def ||
"interface FactorioPrototypes " in def || "interface FactorioPrototypes " in def ||
"interface FactorioPrototype " in def || "interface FactorioPrototype " in def ||
"type PrototypeObjectName " in def -> "prototype.d.ts" "type PrototypeObjectName " in def -> "prototype.d.ts"
"extends FactorioConfigurationUpdate " in def || "extends FactorioConfigurationUpdate " in def ||
"interface FactorioConfigurationUpdate " in def || "interface FactorioConfigurationUpdate " in def ||
"FactorioGameDataUpdate " in def || "FactorioGameDataUpdate " in def ||
"FactorioModInfo " in def -> "config-update.d.ts" "FactorioModInfo " in def -> "config-update.d.ts"
"interface FactorioEventUpdatePacket" in def ||
"type FactorioEventUpdateType =" in def ||
"interface FactorioEventUpdateKey {" in def ||
"interface FactorioEventUpdateData {" in def ||
def.contains(
Regex(
"""
interface.+extends.+(FactorioEventUpdate|PlayerUpdateKey|PlayerUpdateData).*
""".trimIndent()
)
)
-> "update.d.ts"
else -> "schema.d.ts" else -> "schema.d.ts"
} }
} }