mod: entity prototype improvements and refactoring
- refactor and move queue & emitter classes for consistency - don't emit all prototypes at once - add simple queue for 'instant' packets so the printer doesn't get overwhelmed and mix up the output
This commit is contained in:
parent
ae0eb98f12
commit
90aa086dd4
33 changed files with 742 additions and 652 deletions
|
|
@ -1,235 +0,0 @@
|
|||
import KafkatorioSettings from "../settings/KafkatorioSettings";
|
||||
import {KafkatorioKeyedPacketData} from "../types";
|
||||
import packetEmitter from "../PacketEmitter";
|
||||
|
||||
export namespace EventUpdates {
|
||||
|
||||
|
||||
declare const global: {
|
||||
/** Map the hash of the key, as a string, a cache entry */
|
||||
cache: LuaTable<string, CacheEntry<KafkatorioKeyedPacketData>>,
|
||||
}
|
||||
|
||||
|
||||
export class Manager {
|
||||
|
||||
|
||||
init(force?: boolean) {
|
||||
|
||||
const isAnythingUndefined = global.cache == undefined
|
||||
|
||||
log(`Initialising EventDataCache (force=${force}, isAnythingUndefined=${isAnythingUndefined})...`)
|
||||
|
||||
if (force == true || isAnythingUndefined) {
|
||||
log("Initialising global.cache")
|
||||
global.cache = new LuaTable<string, CacheEntry<KafkatorioKeyedPacketData>>()
|
||||
}
|
||||
log(`Finished initialising EventDataCache`)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the cache data without restarting the 'time-to-emit' countdown, so that the data won't
|
||||
* be emitted more than once per {@link CacheEntry.expirationDurationTicks}.
|
||||
*/
|
||||
public throttle<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
this.update(key, type, mutate, false, expirationDurationTicks)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the cache data and reset the 'time-to-emit' countdown, so the data won't be emitted
|
||||
* until there's an inactivity gap of {@link CacheEntry.expirationDurationTicks}.
|
||||
*/
|
||||
public debounce<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
this.update(key, type, mutate, true, expirationDurationTicks)
|
||||
}
|
||||
|
||||
|
||||
private update<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
resetLastUpdated: boolean,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
const entry: CacheEntry<PACKET> = this.getCacheEntry(key, type) ??
|
||||
Manager.createCacheEntry(key, type)
|
||||
mutate(entry.packet)
|
||||
if (resetLastUpdated) {
|
||||
entry.lastUpdatedTick = game.tick
|
||||
}
|
||||
if (expirationDurationTicks != undefined) {
|
||||
entry.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
|
||||
if (entry.expirationDurationTicks != undefined && entry.expirationDurationTicks <= 0) {
|
||||
this.extractAndEmitExpiredPackets()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Usage example: If an urgent update comes in, then the expiration can be reduced so that
|
||||
* it's transmitted sooner.
|
||||
*/
|
||||
public setExpiration<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
expirationDurationTicks: uint,
|
||||
) {
|
||||
const entry = this.getCacheEntry(key, type)
|
||||
if (entry != undefined) {
|
||||
entry.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Removes all expired entries from the cache, and return them. */
|
||||
// use in/out v4.7.0 https://github.com/microsoft/TypeScript/pull/48240
|
||||
private extractExpired(): Array<KafkatorioKeyedPacketData> {
|
||||
let countExpired = 0
|
||||
let countTotal = 0
|
||||
const expiredData: Array<KafkatorioKeyedPacketData> = []
|
||||
for (const [key, entry] of global.cache) {
|
||||
countTotal++
|
||||
if (this.isExpired(entry)) {
|
||||
countExpired++
|
||||
expiredData.push(entry.packet)
|
||||
global.cache.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
const hasExpiredItems = countExpired > 0
|
||||
const hasAnyItem = countTotal > 0
|
||||
|
||||
if (hasExpiredItems || (game.tick % 30 == 0 && hasAnyItem)) {
|
||||
log(`[extractExpired] expired items: ${countExpired}, total: ${countTotal}`)
|
||||
}
|
||||
return expiredData
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and emits all expired packets.
|
||||
*
|
||||
* @return the number of emitted packets
|
||||
*/
|
||||
public extractAndEmitExpiredPackets() {
|
||||
const cachedEvents: Array<KafkatorioKeyedPacketData> = this.extractExpired()
|
||||
|
||||
if (cachedEvents.length > 0) {
|
||||
log(`[EventDataCache] emitting ${cachedEvents.length} events on tick ${game.tick}`)
|
||||
for (const event of cachedEvents) {
|
||||
packetEmitter.emitKeyedPacket(event)
|
||||
}
|
||||
}
|
||||
|
||||
return cachedEvents.length
|
||||
}
|
||||
|
||||
|
||||
private getCacheEntry<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: EventUpdates.PacketKey<PACKET>,
|
||||
type: EventUpdates.PacketType<PACKET>,
|
||||
): CacheEntry<PACKET> | undefined {
|
||||
const hash = Manager.hashKey(key)
|
||||
if (!global.cache.has(hash)) {
|
||||
return undefined
|
||||
}
|
||||
const value: CacheEntry<any> = global.cache.get(hash)
|
||||
if (this.isEntryInstanceOf(value, type)) {
|
||||
return value
|
||||
} else {
|
||||
// Type mismatch. This shouldn't happen...
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static createCacheEntry<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
): CacheEntry<PACKET> {
|
||||
const data: PACKET = <PACKET>{
|
||||
type: type,
|
||||
key: key,
|
||||
}
|
||||
const entry: CacheEntry<PACKET> = new CacheEntry<PACKET>(data)
|
||||
const hash = Manager.hashKey(key)
|
||||
global.cache.set(hash, entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
|
||||
isExpired<TYPE extends KafkatorioKeyedPacketData>(entry: CacheEntry<TYPE>): boolean {
|
||||
const expiryDuration =
|
||||
entry.expirationDurationTicks
|
||||
?? KafkatorioSettings.getEventCacheExpirationTicks(entry.packet)
|
||||
|
||||
if (expiryDuration == undefined) {
|
||||
return true
|
||||
} else {
|
||||
return game.tick - entry.lastUpdatedTick > expiryDuration
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isEntryInstanceOf<PACKET extends KafkatorioKeyedPacketData>(
|
||||
entry: CacheEntry<any> | undefined,
|
||||
type: EventUpdates.PacketType<PACKET>,
|
||||
): entry is CacheEntry<PACKET> {
|
||||
return this.isDataInstanceOf(entry?.packet, type)
|
||||
}
|
||||
|
||||
|
||||
isDataInstanceOf<PACKET extends KafkatorioKeyedPacketData>(
|
||||
packet: PACKET | undefined,
|
||||
type: EventUpdates.PacketType<PACKET>,
|
||||
): packet is PACKET {
|
||||
return packet != undefined && packet.type == type
|
||||
}
|
||||
|
||||
private static hashKey<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
): string {
|
||||
return game.encode_string(game.table_to_json(key))!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type PacketKey<PACKET extends KafkatorioKeyedPacketData> = PACKET["key"]
|
||||
export type PacketType<PACKET extends KafkatorioKeyedPacketData> = PACKET["type"]
|
||||
|
||||
export type CacheDataMutator<PACKET extends KafkatorioKeyedPacketData> = (packet: PACKET) => void
|
||||
|
||||
class CacheEntry<PACKET extends KafkatorioKeyedPacketData> {
|
||||
lastUpdatedTick: uint = game.tick
|
||||
expirationDurationTicks?: uint
|
||||
|
||||
packet: PACKET
|
||||
|
||||
constructor(
|
||||
packet: PACKET,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
|
||||
this.packet = packet
|
||||
this.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EventUpdatesManager = new EventUpdates.Manager()
|
||||
|
||||
export default EventUpdatesManager
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import {initGlobal} from "../global-init";
|
||||
import {Queue} from "../queue/queue";
|
||||
import {emitPrototypes} from "../events/prototypeUpdates";
|
||||
import EventDataQueue from "../queue/queue";
|
||||
|
||||
|
||||
commands.add_command(
|
||||
|
|
@ -58,7 +58,7 @@ commands.add_command(
|
|||
tick: e.tick,
|
||||
}
|
||||
|
||||
Queue.enqueue(
|
||||
EventDataQueue.enqueue(
|
||||
`${surface.index}${chunk.x}${chunk.y}`,
|
||||
data,
|
||||
50
|
||||
|
|
|
|||
|
|
@ -0,0 +1,240 @@
|
|||
import KafkatorioSettings from "../settings/KafkatorioSettings";
|
||||
import {KafkatorioKeyedPacketData} from "../types";
|
||||
import PacketEmitter from "./PacketEmitter";
|
||||
|
||||
|
||||
declare const global: {
|
||||
/** Map the hash of the key, as a string, a cache entry */
|
||||
eventUpdatesManagerCache: LuaTable<string, CacheEntry<KafkatorioKeyedPacketData>>,
|
||||
}
|
||||
|
||||
|
||||
export class EventUpdatesManager {
|
||||
|
||||
constructor() {
|
||||
this.init()
|
||||
}
|
||||
|
||||
|
||||
init(force?: boolean) {
|
||||
|
||||
const isAnythingUndefined = global.eventUpdatesManagerCache == undefined
|
||||
|
||||
log(`Initialising EventDataCache (force=${force}, isAnythingUndefined=${isAnythingUndefined})...`)
|
||||
|
||||
if (force == true || isAnythingUndefined) {
|
||||
log("Initialising global.cache")
|
||||
global.eventUpdatesManagerCache = new LuaTable<string, CacheEntry<KafkatorioKeyedPacketData>>()
|
||||
}
|
||||
log(`Finished initialising EventDataCache`)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the cache data without restarting the 'time-to-emit' countdown, so that the data won't
|
||||
* be emitted more than once per {@link CacheEntry.expirationDurationTicks}.
|
||||
*/
|
||||
public throttle<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
this.update(key, type, mutate, false, expirationDurationTicks)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the cache data and reset the 'time-to-emit' countdown, so the data won't be emitted
|
||||
* until there's an inactivity gap of {@link CacheEntry.expirationDurationTicks}.
|
||||
*/
|
||||
public debounce<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
this.update(key, type, mutate, true, expirationDurationTicks)
|
||||
}
|
||||
|
||||
|
||||
private update<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
mutate: CacheDataMutator<PACKET>,
|
||||
resetLastUpdated: boolean,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
const entry: CacheEntry<PACKET> = this.getCacheEntry(key, type) ??
|
||||
EventUpdatesManager.createCacheEntry(key, type)
|
||||
mutate(entry.packet)
|
||||
if (resetLastUpdated) {
|
||||
entry.lastUpdatedTick = game.tick
|
||||
}
|
||||
if (expirationDurationTicks != undefined) {
|
||||
entry.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
|
||||
if (entry.expirationDurationTicks != undefined && entry.expirationDurationTicks <= 0) {
|
||||
this.extractAndEmitExpiredPackets()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Usage example: If an urgent update comes in, then the expiration can be reduced so that
|
||||
* it's transmitted sooner.
|
||||
*/
|
||||
public setExpiration<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
expirationDurationTicks: uint,
|
||||
) {
|
||||
const entry = this.getCacheEntry(key, type)
|
||||
if (entry != undefined) {
|
||||
entry.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Removes all expired entries from the cache, and return them. */
|
||||
// use in/out v4.7.0 https://github.com/microsoft/TypeScript/pull/48240
|
||||
private extractExpired(): Array<KafkatorioKeyedPacketData> {
|
||||
let countExpired = 0
|
||||
let countTotal = 0
|
||||
const expiredData: Array<KafkatorioKeyedPacketData> = []
|
||||
for (const [key, entry] of global.eventUpdatesManagerCache) {
|
||||
countTotal++
|
||||
if (this.isExpired(entry)) {
|
||||
countExpired++
|
||||
expiredData.push(entry.packet)
|
||||
global.eventUpdatesManagerCache.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
const hasExpiredItems = countExpired > 0
|
||||
const hasAnyItem = countTotal > 0
|
||||
|
||||
if (hasExpiredItems || (game.tick % 30 == 0 && hasAnyItem)) {
|
||||
log(`[extractExpired] expired items: ${countExpired}, total: ${countTotal}`)
|
||||
}
|
||||
return expiredData
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and emits all expired packets.
|
||||
*
|
||||
* @return the number of emitted packets
|
||||
*/
|
||||
public extractAndEmitExpiredPackets() {
|
||||
const cachedEvents: Array<KafkatorioKeyedPacketData> = this.extractExpired()
|
||||
|
||||
if (cachedEvents.length > 0) {
|
||||
log(`[EventDataCache] emitting ${cachedEvents.length} events on tick ${game.tick}`)
|
||||
for (const event of cachedEvents) {
|
||||
PacketEmitter.emitKeyedPacket(event)
|
||||
}
|
||||
}
|
||||
|
||||
return cachedEvents.length
|
||||
}
|
||||
|
||||
|
||||
private getCacheEntry<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
): CacheEntry<PACKET> | undefined {
|
||||
const hash = EventUpdatesManager.hashKey(key)
|
||||
if (!global.eventUpdatesManagerCache.has(hash)) {
|
||||
return undefined
|
||||
}
|
||||
const value: CacheEntry<any> = global.eventUpdatesManagerCache.get(hash)
|
||||
if (this.isEntryInstanceOf(value, type)) {
|
||||
return value
|
||||
} else {
|
||||
// Type mismatch. This shouldn't happen...
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static createCacheEntry<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
type: PacketType<PACKET>,
|
||||
): CacheEntry<PACKET> {
|
||||
const data: PACKET = <PACKET>{
|
||||
type: type,
|
||||
key: key,
|
||||
}
|
||||
const entry: CacheEntry<PACKET> = new CacheEntry<PACKET>(data)
|
||||
const hash = EventUpdatesManager.hashKey(key)
|
||||
global.eventUpdatesManagerCache.set(hash, entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
|
||||
isExpired<TYPE extends KafkatorioKeyedPacketData>(entry: CacheEntry<TYPE>): boolean {
|
||||
const expiryDuration =
|
||||
entry.expirationDurationTicks
|
||||
?? KafkatorioSettings.getEventCacheExpirationTicks(entry.packet)
|
||||
|
||||
if (expiryDuration == undefined) {
|
||||
return true
|
||||
} else {
|
||||
return game.tick - entry.lastUpdatedTick > expiryDuration
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isEntryInstanceOf<PACKET extends KafkatorioKeyedPacketData>(
|
||||
entry: CacheEntry<any> | undefined,
|
||||
type: PacketType<PACKET>,
|
||||
): entry is CacheEntry<PACKET> {
|
||||
return this.isDataInstanceOf(entry?.packet, type)
|
||||
}
|
||||
|
||||
|
||||
isDataInstanceOf<PACKET extends KafkatorioKeyedPacketData>(
|
||||
packet: PACKET | undefined,
|
||||
type: PacketType<PACKET>,
|
||||
): packet is PACKET {
|
||||
return packet != undefined && packet.type == type
|
||||
}
|
||||
|
||||
|
||||
private static hashKey<PACKET extends KafkatorioKeyedPacketData>(
|
||||
key: PacketKey<PACKET>,
|
||||
): string {
|
||||
return game.encode_string(game.table_to_json(key))!!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const EventUpdates = new EventUpdatesManager()
|
||||
|
||||
export default EventUpdates
|
||||
|
||||
|
||||
export type PacketKey<PACKET extends KafkatorioKeyedPacketData> = PACKET["key"]
|
||||
export type PacketType<PACKET extends KafkatorioKeyedPacketData> = PACKET["type"]
|
||||
|
||||
export type CacheDataMutator<PACKET extends KafkatorioKeyedPacketData> = (packet: PACKET) => void
|
||||
|
||||
|
||||
class CacheEntry<PACKET extends KafkatorioKeyedPacketData> {
|
||||
lastUpdatedTick: uint = game.tick
|
||||
expirationDurationTicks?: uint
|
||||
|
||||
packet: PACKET
|
||||
|
||||
constructor(
|
||||
packet: PACKET,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
|
||||
this.packet = packet
|
||||
this.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
|
||||
|
||||
export class KafkatorioPacketQueueManager {
|
||||
|
||||
private size: int = 0
|
||||
private first: Node<any> | null = null
|
||||
private last: Node<any> | null = null
|
||||
|
||||
public get length() {
|
||||
return this.size
|
||||
}
|
||||
|
||||
public enqueue<T extends KafkatorioPacketData>(packet: T) {
|
||||
const last = this.last
|
||||
this.last = {
|
||||
value: packet
|
||||
}
|
||||
if (last != null) {
|
||||
last.next = this.last
|
||||
}
|
||||
if (this.first == null) {
|
||||
this.first = this.last
|
||||
}
|
||||
this.size++
|
||||
}
|
||||
|
||||
public dequeue<T extends KafkatorioPacketData>(): T | null {
|
||||
const first = this.first
|
||||
|
||||
if (first == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
this.first = first.next ?? null
|
||||
this.size--
|
||||
return first.value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const KafkatorioPacketQueue = new KafkatorioPacketQueueManager()
|
||||
|
||||
export default KafkatorioPacketQueue
|
||||
|
||||
interface Node<T extends KafkatorioPacketData> {
|
||||
value: T
|
||||
next?: Node<T>
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
import {
|
||||
KafkatorioPacket,
|
||||
KafkatorioPacketData
|
||||
} from "../generated/kafkatorio-schema";
|
||||
import {KafkatorioKeyedPacketData} from "./types";
|
||||
import {KafkatorioPacket, KafkatorioPacketData} from "../../generated/kafkatorio-schema";
|
||||
import {KafkatorioKeyedPacketData} from "../types";
|
||||
|
||||
|
||||
export class KafkatorioPacketEmitter {
|
||||
|
|
@ -1,33 +1,26 @@
|
|||
import PacketEmitter from "../PacketEmitter";
|
||||
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
|
||||
import Type = KafkatorioPacketData.Type;
|
||||
import KafkatorioPacketQueue from "../emitting/KafkatorioPacketQueue";
|
||||
|
||||
|
||||
script.on_event(
|
||||
defines.events.on_console_chat,
|
||||
(event: OnConsoleChatEvent) => {
|
||||
const update: KafkatorioPacketData.ConsoleChatUpdate = {
|
||||
type: Type.ConsoleChatUpdate,
|
||||
script.on_event(defines.events.on_console_chat, (event: OnConsoleChatEvent) => {
|
||||
const update: KafkatorioPacketData.ConsoleChatUpdate = {
|
||||
type: KafkatorioPacketData.Type.ConsoleChatUpdate,
|
||||
|
||||
authorPlayerIndex: event.player_index ?? null,
|
||||
content: event.message
|
||||
}
|
||||
PacketEmitter.emitInstantPacket(update)
|
||||
}
|
||||
)
|
||||
authorPlayerIndex: event.player_index ?? null,
|
||||
content: event.message
|
||||
}
|
||||
KafkatorioPacketQueue.enqueue(update)
|
||||
})
|
||||
|
||||
|
||||
script.on_event(
|
||||
defines.events.on_console_command,
|
||||
(event: OnConsoleCommandEvent) => {
|
||||
const update: KafkatorioPacketData.ConsoleCommandUpdate = {
|
||||
type: Type.ConsoleCommandUpdate,
|
||||
script.on_event(defines.events.on_console_command, (event: OnConsoleCommandEvent) => {
|
||||
const update: KafkatorioPacketData.ConsoleCommandUpdate = {
|
||||
type: KafkatorioPacketData.Type.ConsoleCommandUpdate,
|
||||
|
||||
authorPlayerIndex: event.player_index ?? null,
|
||||
command: event.command,
|
||||
parameters: event.parameters,
|
||||
}
|
||||
authorPlayerIndex: event.player_index ?? null,
|
||||
command: event.command,
|
||||
parameters: event.parameters,
|
||||
}
|
||||
|
||||
PacketEmitter.emitInstantPacket(update)
|
||||
}
|
||||
)
|
||||
KafkatorioPacketQueue.enqueue(update)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import {
|
|||
ConfigurationUpdateModData,
|
||||
KafkatorioPacketData
|
||||
} from "../../../generated/kafkatorio-schema";
|
||||
import PacketEmitter from "../../PacketEmitter";
|
||||
import PacketEmitter from "../../emitting/PacketEmitter";
|
||||
import ConfigurationUpdate = KafkatorioPacketData.ConfigurationUpdate;
|
||||
import KafkatorioPacketQueue from "../../emitting/KafkatorioPacketQueue";
|
||||
|
||||
|
||||
export class ConfigurationUpdateHandler {
|
||||
|
|
@ -20,7 +21,7 @@ export class ConfigurationUpdateHandler {
|
|||
factorioData: ConfigurationUpdateHandler.factorioData(changeData)
|
||||
}
|
||||
|
||||
PacketEmitter.emitInstantPacket(configUpdateData)
|
||||
KafkatorioPacketQueue.enqueue(configUpdateData)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
Tick
|
||||
} from "../../../generated/kafkatorio-schema";
|
||||
import {Converters} from "../converters";
|
||||
import EventDataCache from "../../cache/EventDataCache";
|
||||
import EventDataCache from "../../emitting/EventDataCache";
|
||||
|
||||
|
||||
export class EntityUpdatesHandler {
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@ import {isEventType} from "../eventTypeCheck";
|
|||
import {
|
||||
KafkatorioPacketData,
|
||||
MapChunkPosition,
|
||||
MinedProduct,
|
||||
PrototypeKey
|
||||
} from "../../../generated/kafkatorio-schema";
|
||||
import EventUpdatesManager, {EventUpdates} from "../../cache/EventDataCache";
|
||||
import EventUpdates, {PacketKey} from "../../emitting/EventDataCache";
|
||||
import {MapChunkUpdateEvent, MapTileChangeEvent} from "../mapChunkUpdates";
|
||||
import PacketKey = EventUpdates.PacketKey;
|
||||
import Type = MinedProduct.Type;
|
||||
|
||||
|
||||
type MapChunkTileUpdater = (data: KafkatorioPacketData.MapChunkTileUpdate) => void
|
||||
|
||||
|
|
@ -141,7 +139,7 @@ class MapChunkUpdateHandler {
|
|||
chunkPosition: [position.x, position.y],
|
||||
}
|
||||
|
||||
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
|
||||
EventUpdates.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
|
||||
key,
|
||||
KafkatorioPacketData.Type.MapChunkTileUpdate,
|
||||
data => data.isDeleted = true,
|
||||
|
|
@ -171,7 +169,7 @@ class MapChunkUpdateHandler {
|
|||
chunkPosition: chunkPosition,
|
||||
}
|
||||
|
||||
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
|
||||
EventUpdates.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
|
||||
key,
|
||||
KafkatorioPacketData.Type.MapChunkTileUpdate,
|
||||
data => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {KafkatorioPacketData} from "../../../generated/kafkatorio-schema";
|
||||
import {Converters} from "../converters";
|
||||
import EventUpdatesManager from "../../cache/EventDataCache";
|
||||
import EventUpdates from "../../emitting/EventDataCache";
|
||||
|
||||
|
||||
export type PlayerUpdater = (player: LuaPlayer, data: KafkatorioPacketData.PlayerUpdate) => void
|
||||
|
|
@ -43,7 +43,7 @@ export class PlayerUpdateHandler {
|
|||
|
||||
const eventName = Converters.eventNameString(event.name)
|
||||
|
||||
EventUpdatesManager.throttle<KafkatorioPacketData.PlayerUpdate>(
|
||||
EventUpdates.throttle<KafkatorioPacketData.PlayerUpdate>(
|
||||
{index: playerIndex},
|
||||
KafkatorioPacketData.Type.PlayerUpdate,
|
||||
data => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {Converters} from "./converters";
|
||||
import {ForceIndex, KafkatorioPacketData, PlayerUpdateKey} from "../../generated/kafkatorio-schema";
|
||||
import packetEmitter from "../PacketEmitter";
|
||||
import packetEmitter from "../emitting/PacketEmitter";
|
||||
import PlayerUpdates from "./handlers/PlayerUpdateHandler";
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import PacketEmitter from "../PacketEmitter";
|
||||
import PacketEmitter from "../emitting/PacketEmitter";
|
||||
import {
|
||||
EntityItemGroup,
|
||||
FactorioPrototype,
|
||||
KafkatorioPacketData
|
||||
} from "../../generated/kafkatorio-schema";
|
||||
import {Converters} from "./converters";
|
||||
import KafkatorioPacketQueue from "../emitting/KafkatorioPacketQueue";
|
||||
|
||||
|
||||
type PrototypesByType<T extends FactorioPrototype> = LuaTable<string, T[]>
|
||||
type PrototypesByType<T extends FactorioPrototype> = Record<string, T[]>
|
||||
|
||||
|
||||
export function emitPrototypes() {
|
||||
|
|
@ -16,10 +17,6 @@ export function emitPrototypes() {
|
|||
emit(mapTileProtos)
|
||||
|
||||
|
||||
game.item_group_prototypes
|
||||
game.item_subgroup_prototypes
|
||||
|
||||
|
||||
const entityProtos: PrototypesByType<FactorioPrototype.Entity> = getEntityPrototypes()
|
||||
emit(entityProtos)
|
||||
}
|
||||
|
|
@ -27,7 +24,7 @@ export function emitPrototypes() {
|
|||
|
||||
function emit<T extends FactorioPrototype>(protosByType: PrototypesByType<T>) {
|
||||
for (const [, protos] of pairs(protosByType)) {
|
||||
PacketEmitter.emitInstantPacket({
|
||||
KafkatorioPacketQueue.enqueue({
|
||||
type: KafkatorioPacketData.Type.PrototypesUpdate,
|
||||
prototypes: protos,
|
||||
})
|
||||
|
|
@ -36,13 +33,10 @@ function emit<T extends FactorioPrototype>(protosByType: PrototypesByType<T>) {
|
|||
|
||||
|
||||
function getMapTilePrototypes(): PrototypesByType<FactorioPrototype.MapTile> {
|
||||
const tiles = new LuaTable<string, FactorioPrototype.MapTile[]>()
|
||||
if (!tiles.has("tile")) {
|
||||
tiles.set("tile", [])
|
||||
}
|
||||
const tiles: FactorioPrototype.MapTile[] = []
|
||||
|
||||
for (const [, tile] of game.tile_prototypes) {
|
||||
tiles.get("tile").push({
|
||||
tiles[tiles.length] = {
|
||||
type: FactorioPrototype.Type.MapTile,
|
||||
|
||||
protoId: Converters.prototypeId("tile", tile.name),
|
||||
|
|
@ -51,21 +45,22 @@ function getMapTilePrototypes(): PrototypesByType<FactorioPrototype.MapTile> {
|
|||
collisionMasks: Converters.convertCollisionMaskToNames(tile.collision_mask),
|
||||
mapColour: Converters.mapColour(tile.map_color),
|
||||
canBeMined: tile.mineable_properties.minable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"tiles": tiles
|
||||
}
|
||||
return tiles
|
||||
}
|
||||
|
||||
|
||||
function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
|
||||
const protos = new LuaTable<string, FactorioPrototype.Entity[]>()
|
||||
const protos: PrototypesByType<FactorioPrototype.Entity> = {}
|
||||
|
||||
for (const [, entity] of game.entity_prototypes) {
|
||||
const key: string = `${entity.group.name}/${entity.subgroup.name}`
|
||||
|
||||
if (!protos.has(key)) {
|
||||
protos.set(key, [])
|
||||
}
|
||||
protos[key] ??= []
|
||||
|
||||
const entityProto: FactorioPrototype.Entity = {
|
||||
type: FactorioPrototype.Type.Entity,
|
||||
|
|
@ -91,7 +86,7 @@ function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
|
|||
}
|
||||
entityProto.miningProperties = Converters.miningProperties(entity.mineable_properties)
|
||||
|
||||
protos.get(key).push(entityProto)
|
||||
protos[key].push(entityProto)
|
||||
}
|
||||
return protos
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,50 @@
|
|||
import {Queue} from "../queue/queue";
|
||||
import {isEventType} from "./eventTypeCheck";
|
||||
import {Converters} from "./converters";
|
||||
import {handleSurfaceUpdate} from "./surfaceUpdates";
|
||||
import EventCacheService from "../cache/EventDataCache";
|
||||
import EventCacheService from "../emitting/EventDataCache";
|
||||
import MapChunkUpdate from "./handlers/MapChunkUpdateHandler";
|
||||
import EventDataQueue from "../queue/queue";
|
||||
import KafkatorioPacketQueue from "../emitting/KafkatorioPacketQueue";
|
||||
import PacketEmitter from "../emitting/PacketEmitter";
|
||||
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
|
||||
|
||||
|
||||
script.on_event(
|
||||
defines.events.on_tick,
|
||||
(event: OnTickEvent) => {
|
||||
if (event.tick % 1000 == 0) {
|
||||
for (const [, surface] of game.surfaces) {
|
||||
handleSurfaceUpdate(event, Converters.eventNameString(event.name), surface)
|
||||
}
|
||||
}
|
||||
script.on_event(defines.events.on_tick, (event: OnTickEvent) => {
|
||||
if (event.tick % 1000 == 0) {
|
||||
for (const [, surface] of game.surfaces) {
|
||||
handleSurfaceUpdate(event, Converters.eventNameString(event.name), surface)
|
||||
}
|
||||
}
|
||||
|
||||
if (event.tick % 6 == 0) {
|
||||
// Every 0.1 seconds
|
||||
const emittedCount = EventCacheService.extractAndEmitExpiredPackets()
|
||||
if (emittedCount > 0) {
|
||||
log(`[on_tick:${event.tick}] emitted ${emittedCount} events`)
|
||||
}
|
||||
}
|
||||
const kPacket: KafkatorioPacketData | null = KafkatorioPacketQueue.dequeue()
|
||||
if (kPacket != null) {
|
||||
PacketEmitter.emitInstantPacket(kPacket)
|
||||
}
|
||||
|
||||
if (event.tick % 7 == 0) {
|
||||
const events: EventData[] = Queue.dequeueValues(1)
|
||||
if (event.tick % 6 == 0) {
|
||||
// Every 0.1 seconds
|
||||
const emittedCount = EventCacheService.extractAndEmitExpiredPackets()
|
||||
if (emittedCount > 0) {
|
||||
log(`[on_tick:${event.tick}] emitted ${emittedCount} events`)
|
||||
}
|
||||
}
|
||||
|
||||
if (events.length > 0) {
|
||||
log(`[on_tick:${event.tick}] dequeued ${events.length} events, current size: ${Queue.size()}`)
|
||||
if (event.tick % 7 == 0) {
|
||||
const events: EventData[] = EventDataQueue.dequeueValues(1)
|
||||
|
||||
let i = 1
|
||||
for (const event of events) {
|
||||
if (isEventType(event, defines.events.on_chunk_generated)) {
|
||||
let eName = Converters.eventNameString(event.name)
|
||||
if (events.length > 0) {
|
||||
log(`[on_tick:${event.tick}] dequeued ${events.length} events, current size: ${EventDataQueue.size()}`)
|
||||
|
||||
log(`[on_tick:${event.tick}] dequed event ${eName}, delay ${i}`)
|
||||
MapChunkUpdate.handleChunkGeneratedEvent(event, i)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
let i = 1
|
||||
for (const event of events) {
|
||||
if (isEventType(event, defines.events.on_chunk_generated)) {
|
||||
let eName = Converters.eventNameString(event.name)
|
||||
|
||||
log(`[on_tick:${event.tick}] dequed event ${eName}, delay ${i}`)
|
||||
MapChunkUpdate.handleChunkGeneratedEvent(event, i)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
script.on_event(
|
||||
defines.events.on_runtime_mod_setting_changed,
|
||||
(e: OnRuntimeModSettingChangedEvent) => {
|
||||
print(`Settings changed ${e.setting_type}, ${e.player_index}, ${e.setting}, ${e.mod_name}`)
|
||||
// KafkatorioSettings.loadSettings()
|
||||
}
|
||||
)
|
||||
script.on_event(defines.events.on_runtime_mod_setting_changed, (e: OnRuntimeModSettingChangedEvent) => {
|
||||
print(`Settings changed ${e.setting_type}, ${e.player_index}, ${e.setting}, ${e.mod_name}`)
|
||||
// KafkatorioSettings.loadSettings()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import PacketEmitter from "../PacketEmitter";
|
||||
import PacketEmitter from "../emitting/PacketEmitter";
|
||||
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
|
||||
import Type = KafkatorioPacketData.Type;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {Queue} from "./queue/queue";
|
||||
import EventUpdatesManager from "./cache/EventDataCache";
|
||||
import EventUpdates from "./emitting/EventDataCache";
|
||||
import {EventDataQueueManager} from "./queue/queue";
|
||||
|
||||
|
||||
export function initGlobal(force: boolean = false) {
|
||||
|
|
@ -8,8 +8,8 @@ export function initGlobal(force: boolean = false) {
|
|||
global.MOD_VERSION = script.active_mods[script.mod_name]
|
||||
global.FACTORIO_VERSION = script.active_mods["base"]
|
||||
|
||||
Queue.init(true)
|
||||
EventUpdatesManager.init(true)
|
||||
EventDataQueueManager.init(true)
|
||||
EventUpdates.init(true)
|
||||
|
||||
log("Finished initialising Kafkatorio Global variables")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
export namespace Queue {
|
||||
export class EventDataQueueManager {
|
||||
|
||||
declare const global: {
|
||||
store: EventQueueStore
|
||||
|
||||
head: Node | null
|
||||
tail: Node | null
|
||||
|
||||
size: uint
|
||||
}
|
||||
|
||||
|
||||
export function init(force?: boolean) {
|
||||
static init(force?: boolean) {
|
||||
|
||||
const isAnythingUndefined = global.store == undefined ||
|
||||
global.size == undefined ||
|
||||
global.head == undefined ||
|
||||
global.tail == undefined
|
||||
|
||||
log(`Initialising EventQueue globals (force=${force}, isAnythingUndefined=${isAnythingUndefined})`)
|
||||
log(`Initialising EventDataQueueManager globals (force=${force}, isAnythingUndefined=${isAnythingUndefined})`)
|
||||
|
||||
if (force == true || isAnythingUndefined) {
|
||||
global.store = {}
|
||||
|
|
@ -26,16 +16,16 @@ export namespace Queue {
|
|||
global.size = 0
|
||||
}
|
||||
|
||||
log(`Finished initialising EventQueue`)
|
||||
log(`Finished initialising EventDataQueueManager`)
|
||||
}
|
||||
|
||||
|
||||
export function size(): uint | undefined {
|
||||
size(): uint | undefined {
|
||||
return global.size
|
||||
}
|
||||
|
||||
|
||||
export function enqueue(key: string, event: EventData, weight: int = 1) {
|
||||
enqueue(key: string, event: EventData, weight: int = 1) {
|
||||
if (key ! in global.store) {
|
||||
let node = new Node(key, weight)
|
||||
if (global.head == null) {
|
||||
|
|
@ -52,7 +42,7 @@ export namespace Queue {
|
|||
}
|
||||
|
||||
|
||||
export function dequeue(): WeightedPacket | null {
|
||||
dequeue(): WeightedPacket | null {
|
||||
if (global.head == null) {
|
||||
return null
|
||||
} else {
|
||||
|
|
@ -76,12 +66,12 @@ export namespace Queue {
|
|||
}
|
||||
|
||||
|
||||
export function dequeueValues(targetWeight: uint = 50): EventData[] {
|
||||
dequeueValues(targetWeight: uint = 50): EventData[] {
|
||||
let values: EventData[] = []
|
||||
let weight: uint = 0
|
||||
|
||||
do {
|
||||
let packet = dequeue()
|
||||
let packet = this.dequeue()
|
||||
|
||||
if (packet == null) {
|
||||
break;
|
||||
|
|
@ -94,27 +84,43 @@ export namespace Queue {
|
|||
return values
|
||||
}
|
||||
|
||||
|
||||
class Node {
|
||||
storeKey: string;
|
||||
next: Node | null = null;
|
||||
weight: uint
|
||||
|
||||
constructor(storeKey: string, weight: uint) {
|
||||
this.storeKey = storeKey;
|
||||
this.weight = weight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface EventQueueStore {
|
||||
[key: string]: EventData | null;
|
||||
}
|
||||
|
||||
|
||||
interface WeightedPacket {
|
||||
value: EventData,
|
||||
weight: int,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const EventDataQueue = new EventDataQueueManager()
|
||||
|
||||
|
||||
export default EventDataQueue
|
||||
|
||||
|
||||
declare const global: {
|
||||
store: EventQueueStore
|
||||
|
||||
head: Node | null
|
||||
tail: Node | null
|
||||
|
||||
size: uint
|
||||
}
|
||||
|
||||
|
||||
class Node {
|
||||
storeKey: string;
|
||||
next: Node | null = null;
|
||||
weight: uint
|
||||
|
||||
constructor(storeKey: string, weight: uint) {
|
||||
this.storeKey = storeKey;
|
||||
this.weight = weight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface EventQueueStore {
|
||||
[key: string]: EventData | null;
|
||||
}
|
||||
|
||||
|
||||
interface WeightedPacket {
|
||||
value: EventData,
|
||||
weight: int,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue