create an event-cacher, for debouncing/throttling
This commit is contained in:
parent
c53d1d7c87
commit
16510488da
3 changed files with 179 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import {handleConfigurationUpdate} from "./main/config-update/handleConfigurationUpdate";
|
||||
import {emitPrototypes} from "./main/config-update/prototypes";
|
||||
import {Queue} from "./main/queue/queue";
|
||||
import {EventDataCache} from "./main/cache/EventDataCache";
|
||||
|
||||
require("./main/events/registerHandlers")
|
||||
require("./main/commands/kafkatorioCommands")
|
||||
|
|
@ -10,4 +11,5 @@ script.on_configuration_changed(handleConfigurationUpdate)
|
|||
script.on_init(() => {
|
||||
Queue.reset()
|
||||
emitPrototypes()
|
||||
EventDataCache.init()
|
||||
})
|
||||
|
|
|
|||
169
modules/events-mod/src/main/typescript/main/cache/EventDataCache.ts
vendored
Normal file
169
modules/events-mod/src/main/typescript/main/cache/EventDataCache.ts
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import {ExcludeNullKeys} from "../global";
|
||||
|
||||
export namespace EventDataCache {
|
||||
|
||||
|
||||
declare const global: {
|
||||
cache: LuaTable<CacheKey<FactorioEventUpdate>, CacheEntry<FactorioEventUpdate>>,
|
||||
DEFAULT_EXPIRATION_DURATION_TICKS: uint,
|
||||
}
|
||||
|
||||
|
||||
export function init() {
|
||||
if (typeof global.cache == undefined) {
|
||||
global.cache = new LuaTable<CacheKey<FactorioEventUpdate>, CacheEntry<FactorioEventUpdate>>()
|
||||
|
||||
global.DEFAULT_EXPIRATION_DURATION_TICKS = 60 * 60 // ~60 seconds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
export function throttle<T extends FactorioEventUpdate>(
|
||||
key: CacheKey<T>,
|
||||
mutate: (data: CacheData<T> | undefined) => void,
|
||||
) {
|
||||
update(key, mutate, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
export function debounce<T extends FactorioEventUpdate>(
|
||||
key: CacheKey<T>,
|
||||
mutate: (data: CacheData<T> | undefined) => void,
|
||||
) {
|
||||
update(key, mutate, true)
|
||||
}
|
||||
|
||||
function update<T extends FactorioEventUpdate>(
|
||||
key: CacheKey<T>,
|
||||
mutate: (data: CacheData<T> | undefined) => void,
|
||||
resetLastUpdated: boolean,
|
||||
) {
|
||||
const entry = getCacheEntry(key)
|
||||
mutate(entry?.data)
|
||||
if (entry != undefined && resetLastUpdated) {
|
||||
entry.lastUpdatedTick = game.tick
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage example: If an urgent update comes in, then the expiration can be reduced so that
|
||||
* it's transmitted sooner.
|
||||
*/
|
||||
export function setExpiration(
|
||||
key: CacheKey<any>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
const entry = getCacheEntry(key)
|
||||
if (entry != undefined) {
|
||||
entry.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
|
||||
function getCacheEntry<T extends FactorioEventUpdate>(
|
||||
key: CacheKey<T>
|
||||
): CacheEntry<T> | undefined {
|
||||
if (!global.cache.has(key)) {
|
||||
return undefined
|
||||
}
|
||||
const value: CacheEntry<any> = global.cache.get(key)
|
||||
if (isType(value, key.updateType)) {
|
||||
return value
|
||||
} else {
|
||||
// Type mismatch. This shouldn't happen...
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
type UpdateType<T extends FactorioEventUpdate> = T["updateType"]
|
||||
|
||||
/** Only include the non-null properties */
|
||||
export type CacheKey<T extends FactorioEventUpdate> = ExcludeNullKeys<T> & {
|
||||
updateType: UpdateType<T>
|
||||
}
|
||||
|
||||
/** Make the generated schema objects generic closed-polymorphic */
|
||||
export type CacheData<T extends FactorioEventUpdate> = Omit<T, "type"> & {
|
||||
updateType: UpdateType<T>
|
||||
}
|
||||
|
||||
|
||||
class CacheEntry<T extends FactorioEventUpdate> {
|
||||
lastUpdatedTick: uint = game.tick
|
||||
expirationDurationTicks?: uint
|
||||
|
||||
data: CacheData<T>
|
||||
|
||||
constructor(
|
||||
data: CacheData<T>,
|
||||
expirationDurationTicks?: uint,
|
||||
) {
|
||||
this.data = data
|
||||
if (expirationDurationTicks != undefined) {
|
||||
this.expirationDurationTicks = expirationDurationTicks
|
||||
}
|
||||
}
|
||||
|
||||
isExpired(): boolean {
|
||||
return game.tick -
|
||||
this.lastUpdatedTick >
|
||||
(this.expirationDurationTicks ?? global.DEFAULT_EXPIRATION_DURATION_TICKS)
|
||||
}
|
||||
}
|
||||
|
||||
function isType<T extends FactorioEventUpdate>(
|
||||
data: CacheEntry<T>,
|
||||
updateType: UpdateType<T>,
|
||||
): data is CacheEntry<T> {
|
||||
return data.data.updateType == updateType
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// interface FactorioEventUpdate {
|
||||
// type: string
|
||||
// }
|
||||
//
|
||||
// interface Update<T extends FactorioEventUpdateType> {
|
||||
// type: T
|
||||
// }
|
||||
//
|
||||
// export interface PlayerUpdate extends Update<"PLAYER"> {
|
||||
// index: uint
|
||||
//
|
||||
// unhandledEvents?: string[]
|
||||
//
|
||||
// characterUnitNumber?: uint
|
||||
// chatColour?: Colour
|
||||
// colour?: Colour
|
||||
// disconnectReason?: string
|
||||
// forceIndex?: uint
|
||||
// isAdmin?: boolean
|
||||
// isConnected?: boolean
|
||||
// isShowOnMap?: boolean
|
||||
// isSpectator?: boolean
|
||||
// lastOnline?: uint
|
||||
// afkTime?: uint
|
||||
// name?: string
|
||||
// onlineTime?: uint
|
||||
// position?: MapEntityPosition
|
||||
// surfaceIndex?: uint
|
||||
// ticksToRespawn?: uint
|
||||
// tag?: string
|
||||
// }
|
||||
|
||||
// export type CacheKey<Type> = MapChunkCacheKey | EntityCacheKey | PlayerCacheKey & {
|
||||
// type: Type
|
||||
// }
|
||||
//
|
||||
// export function isCacheType<CK extends CacheKey>(
|
||||
// cacheKey: CacheKey,
|
||||
// type: CacheType
|
||||
// ): cacheKey is CK {
|
||||
// return cacheKey.type == type
|
||||
// }
|
||||
|
|
@ -2,3 +2,11 @@ declare const global: {
|
|||
MOD_VERSION: string
|
||||
FACTORIO_VERSION: string
|
||||
}
|
||||
|
||||
export type EventName = keyof typeof defines.events
|
||||
|
||||
type NonNullKeys<T> = {
|
||||
[P in keyof T]: null extends Exclude<T[P], undefined> ? never : P
|
||||
}[keyof T];
|
||||
|
||||
type ExcludeNullKeys<T> = Pick<T, NonNullKeys<T>>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue