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:
Adam 2022-07-09 14:03:57 +02:00
parent ae0eb98f12
commit 90aa086dd4
33 changed files with 742 additions and 652 deletions

View file

@ -1,144 +0,0 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local ____KafkatorioSettings = require("main.settings.KafkatorioSettings")
local KafkatorioSettings = ____KafkatorioSettings.default
local ____PacketEmitter = require("main.PacketEmitter")
local packetEmitter = ____PacketEmitter.default
____exports.EventUpdates = {}
local EventUpdates = ____exports.EventUpdates
do
local CacheEntry
EventUpdates.Manager = __TS__Class()
local Manager = EventUpdates.Manager
Manager.name = "Manager"
function Manager.prototype.____constructor(self)
end
function Manager.prototype.init(self, force)
local isAnythingUndefined = global.cache == nil
log(((("Initialising EventDataCache (force=" .. tostring(force)) .. ", isAnythingUndefined=") .. tostring(isAnythingUndefined)) .. ")...")
if force == true or isAnythingUndefined then
log("Initialising global.cache")
global.cache = {}
end
log("Finished initialising EventDataCache")
end
function Manager.prototype.throttle(self, key, ____type, mutate, expirationDurationTicks)
self:update(
key,
____type,
mutate,
false,
expirationDurationTicks
)
end
function Manager.prototype.debounce(self, key, ____type, mutate, expirationDurationTicks)
self:update(
key,
____type,
mutate,
true,
expirationDurationTicks
)
end
function Manager.prototype.update(self, key, ____type, mutate, resetLastUpdated, expirationDurationTicks)
local entry = self:getCacheEntry(key, ____type) or EventUpdates.Manager:createCacheEntry(key, ____type)
mutate(entry.packet)
if resetLastUpdated then
entry.lastUpdatedTick = game.tick
end
if expirationDurationTicks ~= nil then
entry.expirationDurationTicks = expirationDurationTicks
end
if entry.expirationDurationTicks ~= nil and entry.expirationDurationTicks <= 0 then
self:extractAndEmitExpiredPackets()
end
end
function Manager.prototype.setExpiration(self, key, ____type, expirationDurationTicks)
local entry = self:getCacheEntry(key, ____type)
if entry ~= nil then
entry.expirationDurationTicks = expirationDurationTicks
end
end
function Manager.prototype.extractExpired(self)
local countExpired = 0
local countTotal = 0
local expiredData = {}
for key, entry in pairs(global.cache) do
countTotal = countTotal + 1
if self:isExpired(entry) then
countExpired = countExpired + 1
expiredData[#expiredData + 1] = entry.packet
global.cache[key] = nil
end
end
local hasExpiredItems = countExpired > 0
local hasAnyItem = countTotal > 0
if hasExpiredItems or game.tick % 30 == 0 and hasAnyItem then
log((("[extractExpired] expired items: " .. tostring(countExpired)) .. ", total: ") .. tostring(countTotal))
end
return expiredData
end
function Manager.prototype.extractAndEmitExpiredPackets(self)
local cachedEvents = self:extractExpired()
if #cachedEvents > 0 then
log((("[EventDataCache] emitting " .. tostring(#cachedEvents)) .. " events on tick ") .. tostring(game.tick))
for ____, event in ipairs(cachedEvents) do
packetEmitter:emitKeyedPacket(event)
end
end
return #cachedEvents
end
function Manager.prototype.getCacheEntry(self, key, ____type)
local hash = EventUpdates.Manager:hashKey(key)
if not (global.cache[hash] ~= nil) then
return nil
end
local value = global.cache[hash]
if self:isEntryInstanceOf(value, ____type) then
return value
else
return nil
end
end
function Manager.createCacheEntry(self, key, ____type)
local data = {type = ____type, key = key}
local entry = __TS__New(CacheEntry, data)
local hash = EventUpdates.Manager:hashKey(key)
global.cache[hash] = entry
return entry
end
function Manager.prototype.isExpired(self, entry)
local expiryDuration = entry.expirationDurationTicks or KafkatorioSettings:getEventCacheExpirationTicks(entry.packet)
if expiryDuration == nil then
return true
else
return game.tick - entry.lastUpdatedTick > expiryDuration
end
end
function Manager.prototype.isEntryInstanceOf(self, entry, ____type)
local ____self_isDataInstanceOf_2 = self.isDataInstanceOf
local ____entry_packet_0 = entry
if ____entry_packet_0 ~= nil then
____entry_packet_0 = ____entry_packet_0.packet
end
return ____self_isDataInstanceOf_2(self, ____entry_packet_0, ____type)
end
function Manager.prototype.isDataInstanceOf(self, packet, ____type)
return packet ~= nil and packet.type == ____type
end
function Manager.hashKey(self, key)
return game.encode_string(game.table_to_json(key))
end
CacheEntry = __TS__Class()
CacheEntry.name = "CacheEntry"
function CacheEntry.prototype.____constructor(self, packet, expirationDurationTicks)
self.lastUpdatedTick = game.tick
self.packet = packet
self.expirationDurationTicks = expirationDurationTicks
end
end
local EventUpdatesManager = __TS__New(____exports.EventUpdates.Manager)
____exports.default = EventUpdatesManager
return ____exports

View file

@ -5,10 +5,10 @@ local __TS__ParseInt = ____lualib.__TS__ParseInt
local ____exports = {} local ____exports = {}
local ____global_2Dinit = require("main.global-init") local ____global_2Dinit = require("main.global-init")
local initGlobal = ____global_2Dinit.initGlobal local initGlobal = ____global_2Dinit.initGlobal
local ____queue = require("main.queue.queue")
local Queue = ____queue.Queue
local ____prototypeUpdates = require("main.events.prototypeUpdates") local ____prototypeUpdates = require("main.events.prototypeUpdates")
local emitPrototypes = ____prototypeUpdates.emitPrototypes local emitPrototypes = ____prototypeUpdates.emitPrototypes
local ____queue = require("main.queue.queue")
local EventDataQueue = ____queue.default
commands.add_command( commands.add_command(
"kafkatorio", "kafkatorio",
"kafkatorio innit bruv", "kafkatorio innit bruv",
@ -56,7 +56,7 @@ commands.add_command(
surface = surface, surface = surface,
tick = e.tick tick = e.tick
} }
Queue.enqueue( EventDataQueue:enqueue(
(tostring(surface.index) .. tostring(chunk.x)) .. tostring(chunk.y), (tostring(surface.index) .. tostring(chunk.x)) .. tostring(chunk.y),
data, data,
50 50

View file

@ -0,0 +1,141 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local CacheEntry
local ____KafkatorioSettings = require("main.settings.KafkatorioSettings")
local KafkatorioSettings = ____KafkatorioSettings.default
local ____PacketEmitter = require("main.emitting.PacketEmitter")
local PacketEmitter = ____PacketEmitter.default
____exports.EventUpdatesManager = __TS__Class()
local EventUpdatesManager = ____exports.EventUpdatesManager
EventUpdatesManager.name = "EventUpdatesManager"
function EventUpdatesManager.prototype.____constructor(self)
self:init()
end
function EventUpdatesManager.prototype.init(self, force)
local isAnythingUndefined = global.eventUpdatesManagerCache == nil
log(((("Initialising EventDataCache (force=" .. tostring(force)) .. ", isAnythingUndefined=") .. tostring(isAnythingUndefined)) .. ")...")
if force == true or isAnythingUndefined then
log("Initialising global.cache")
global.eventUpdatesManagerCache = {}
end
log("Finished initialising EventDataCache")
end
function EventUpdatesManager.prototype.throttle(self, key, ____type, mutate, expirationDurationTicks)
self:update(
key,
____type,
mutate,
false,
expirationDurationTicks
)
end
function EventUpdatesManager.prototype.debounce(self, key, ____type, mutate, expirationDurationTicks)
self:update(
key,
____type,
mutate,
true,
expirationDurationTicks
)
end
function EventUpdatesManager.prototype.update(self, key, ____type, mutate, resetLastUpdated, expirationDurationTicks)
local entry = self:getCacheEntry(key, ____type) or ____exports.EventUpdatesManager:createCacheEntry(key, ____type)
mutate(entry.packet)
if resetLastUpdated then
entry.lastUpdatedTick = game.tick
end
if expirationDurationTicks ~= nil then
entry.expirationDurationTicks = expirationDurationTicks
end
if entry.expirationDurationTicks ~= nil and entry.expirationDurationTicks <= 0 then
self:extractAndEmitExpiredPackets()
end
end
function EventUpdatesManager.prototype.setExpiration(self, key, ____type, expirationDurationTicks)
local entry = self:getCacheEntry(key, ____type)
if entry ~= nil then
entry.expirationDurationTicks = expirationDurationTicks
end
end
function EventUpdatesManager.prototype.extractExpired(self)
local countExpired = 0
local countTotal = 0
local expiredData = {}
for key, entry in pairs(global.eventUpdatesManagerCache) do
countTotal = countTotal + 1
if self:isExpired(entry) then
countExpired = countExpired + 1
expiredData[#expiredData + 1] = entry.packet
global.eventUpdatesManagerCache[key] = nil
end
end
local hasExpiredItems = countExpired > 0
local hasAnyItem = countTotal > 0
if hasExpiredItems or game.tick % 30 == 0 and hasAnyItem then
log((("[extractExpired] expired items: " .. tostring(countExpired)) .. ", total: ") .. tostring(countTotal))
end
return expiredData
end
function EventUpdatesManager.prototype.extractAndEmitExpiredPackets(self)
local cachedEvents = self:extractExpired()
if #cachedEvents > 0 then
log((("[EventDataCache] emitting " .. tostring(#cachedEvents)) .. " events on tick ") .. tostring(game.tick))
for ____, event in ipairs(cachedEvents) do
PacketEmitter:emitKeyedPacket(event)
end
end
return #cachedEvents
end
function EventUpdatesManager.prototype.getCacheEntry(self, key, ____type)
local hash = ____exports.EventUpdatesManager:hashKey(key)
if not (global.eventUpdatesManagerCache[hash] ~= nil) then
return nil
end
local value = global.eventUpdatesManagerCache[hash]
if self:isEntryInstanceOf(value, ____type) then
return value
else
return nil
end
end
function EventUpdatesManager.createCacheEntry(self, key, ____type)
local data = {type = ____type, key = key}
local entry = __TS__New(CacheEntry, data)
local hash = ____exports.EventUpdatesManager:hashKey(key)
global.eventUpdatesManagerCache[hash] = entry
return entry
end
function EventUpdatesManager.prototype.isExpired(self, entry)
local expiryDuration = entry.expirationDurationTicks or KafkatorioSettings:getEventCacheExpirationTicks(entry.packet)
if expiryDuration == nil then
return true
else
return game.tick - entry.lastUpdatedTick > expiryDuration
end
end
function EventUpdatesManager.prototype.isEntryInstanceOf(self, entry, ____type)
local ____self_isDataInstanceOf_2 = self.isDataInstanceOf
local ____entry_packet_0 = entry
if ____entry_packet_0 ~= nil then
____entry_packet_0 = ____entry_packet_0.packet
end
return ____self_isDataInstanceOf_2(self, ____entry_packet_0, ____type)
end
function EventUpdatesManager.prototype.isDataInstanceOf(self, packet, ____type)
return packet ~= nil and packet.type == ____type
end
function EventUpdatesManager.hashKey(self, key)
return game.encode_string(game.table_to_json(key))
end
local EventUpdates = __TS__New(____exports.EventUpdatesManager)
____exports.default = EventUpdates
CacheEntry = __TS__Class()
CacheEntry.name = "CacheEntry"
function CacheEntry.prototype.____constructor(self, packet, expirationDurationTicks)
self.lastUpdatedTick = game.tick
self.packet = packet
self.expirationDurationTicks = expirationDurationTicks
end
return ____exports

View file

@ -0,0 +1,44 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__SetDescriptor = ____lualib.__TS__SetDescriptor
local __TS__New = ____lualib.__TS__New
local ____exports = {}
____exports.KafkatorioPacketQueueManager = __TS__Class()
local KafkatorioPacketQueueManager = ____exports.KafkatorioPacketQueueManager
KafkatorioPacketQueueManager.name = "KafkatorioPacketQueueManager"
function KafkatorioPacketQueueManager.prototype.____constructor(self)
self.size = 0
self.first = nil
self.last = nil
end
__TS__SetDescriptor(
KafkatorioPacketQueueManager.prototype,
"length",
{get = function(self)
return self.size
end},
true
)
function KafkatorioPacketQueueManager.prototype.enqueue(self, packet)
local last = self.last
self.last = {value = packet}
if last ~= nil then
last.next = self.last
end
if self.first == nil then
self.first = self.last
end
self.size = self.size + 1
end
function KafkatorioPacketQueueManager.prototype.dequeue(self)
local first = self.first
if first == nil then
return nil
end
self.first = first.next or nil
self.size = self.size - 1
return first.value
end
local KafkatorioPacketQueue = __TS__New(____exports.KafkatorioPacketQueueManager)
____exports.default = KafkatorioPacketQueue
return ____exports

View file

@ -1,21 +1,20 @@
local ____exports = {} local ____exports = {}
local ____PacketEmitter = require("main.PacketEmitter")
local PacketEmitter = ____PacketEmitter.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local Type = KafkatorioPacketData.Type local ____KafkatorioPacketQueue = require("main.emitting.KafkatorioPacketQueue")
local KafkatorioPacketQueue = ____KafkatorioPacketQueue.default
script.on_event( script.on_event(
defines.events.on_console_chat, defines.events.on_console_chat,
function(event) function(event)
local update = {type = Type.ConsoleChatUpdate, authorPlayerIndex = event.player_index or nil, content = event.message} local update = {type = KafkatorioPacketData.Type.ConsoleChatUpdate, authorPlayerIndex = event.player_index or nil, content = event.message}
PacketEmitter:emitInstantPacket(update) KafkatorioPacketQueue:enqueue(update)
end end
) )
script.on_event( script.on_event(
defines.events.on_console_command, defines.events.on_console_command,
function(event) function(event)
local update = {type = Type.ConsoleCommandUpdate, authorPlayerIndex = event.player_index or nil, command = event.command, parameters = event.parameters} local update = {type = KafkatorioPacketData.Type.ConsoleCommandUpdate, authorPlayerIndex = event.player_index or nil, command = event.command, parameters = event.parameters}
PacketEmitter:emitInstantPacket(update) KafkatorioPacketQueue:enqueue(update)
end end
) )
return ____exports return ____exports

View file

@ -9,8 +9,8 @@ local __TS__ArrayMap = ____lualib.__TS__ArrayMap
local ____exports = {} local ____exports = {}
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____PacketEmitter = require("main.PacketEmitter") local ____KafkatorioPacketQueue = require("main.emitting.KafkatorioPacketQueue")
local PacketEmitter = ____PacketEmitter.default local KafkatorioPacketQueue = ____KafkatorioPacketQueue.default
____exports.ConfigurationUpdateHandler = __TS__Class() ____exports.ConfigurationUpdateHandler = __TS__Class()
local ConfigurationUpdateHandler = ____exports.ConfigurationUpdateHandler local ConfigurationUpdateHandler = ____exports.ConfigurationUpdateHandler
ConfigurationUpdateHandler.name = "ConfigurationUpdateHandler" ConfigurationUpdateHandler.name = "ConfigurationUpdateHandler"
@ -24,7 +24,7 @@ function ConfigurationUpdateHandler.prototype.emitConfigurationUpdate(self, chan
allMods = ____exports.ConfigurationUpdateHandler:allMods(changeData), allMods = ____exports.ConfigurationUpdateHandler:allMods(changeData),
factorioData = ____exports.ConfigurationUpdateHandler:factorioData(changeData) factorioData = ____exports.ConfigurationUpdateHandler:factorioData(changeData)
} }
PacketEmitter:emitInstantPacket(configUpdateData) KafkatorioPacketQueue:enqueue(configUpdateData)
end end
function ConfigurationUpdateHandler.factorioData(self, changeData) function ConfigurationUpdateHandler.factorioData(self, changeData)
return {oldVersion = changeData.old_version or nil, newVersion = changeData.new_version or nil} return {oldVersion = changeData.old_version or nil, newVersion = changeData.new_version or nil}

View file

@ -7,7 +7,7 @@ local FactorioEntityData = ____kafkatorio_2Dschema.FactorioEntityData
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____converters = require("main.events.converters") local ____converters = require("main.events.converters")
local Converters = ____converters.Converters local Converters = ____converters.Converters
local ____EventDataCache = require("main.cache.EventDataCache") local ____EventDataCache = require("main.emitting.EventDataCache")
local EventDataCache = ____EventDataCache.default local EventDataCache = ____EventDataCache.default
____exports.EntityUpdatesHandler = __TS__Class() ____exports.EntityUpdatesHandler = __TS__Class()
local EntityUpdatesHandler = ____exports.EntityUpdatesHandler local EntityUpdatesHandler = ____exports.EntityUpdatesHandler

View file

@ -10,8 +10,8 @@ local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType local isEventType = ____eventTypeCheck.isEventType
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____EventDataCache = require("main.cache.EventDataCache") local ____EventDataCache = require("main.emitting.EventDataCache")
local EventUpdatesManager = ____EventDataCache.default local EventUpdates = ____EventDataCache.default
local MapChunkUpdateHandler = __TS__Class() local MapChunkUpdateHandler = __TS__Class()
MapChunkUpdateHandler.name = "MapChunkUpdateHandler" MapChunkUpdateHandler.name = "MapChunkUpdateHandler"
function MapChunkUpdateHandler.prototype.____constructor(self) function MapChunkUpdateHandler.prototype.____constructor(self)
@ -124,7 +124,7 @@ function MapChunkUpdateHandler.prototype.handlePreChunkDeleted(self, event)
end end
for ____, position in ipairs(event.positions) do for ____, position in ipairs(event.positions) do
local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}} local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}}
EventUpdatesManager:debounce( EventUpdates:debounce(
key, key,
KafkatorioPacketData.Type.MapChunkTileUpdate, KafkatorioPacketData.Type.MapChunkTileUpdate,
function(data) function(data)
@ -141,7 +141,7 @@ function MapChunkUpdateHandler.mapTilesUpdateDebounce(self, surface, chunkPositi
end end
local eventName = Converters.eventNameString(event.name) local eventName = Converters.eventNameString(event.name)
local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition} local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition}
EventUpdatesManager:debounce( EventUpdates:debounce(
key, key,
KafkatorioPacketData.Type.MapChunkTileUpdate, KafkatorioPacketData.Type.MapChunkTileUpdate,
function(data) function(data)

View file

@ -6,8 +6,8 @@ local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____converters = require("main.events.converters") local ____converters = require("main.events.converters")
local Converters = ____converters.Converters local Converters = ____converters.Converters
local ____EventDataCache = require("main.cache.EventDataCache") local ____EventDataCache = require("main.emitting.EventDataCache")
local EventUpdatesManager = ____EventDataCache.default local EventUpdates = ____EventDataCache.default
____exports.PlayerUpdateHandler = __TS__Class() ____exports.PlayerUpdateHandler = __TS__Class()
local PlayerUpdateHandler = ____exports.PlayerUpdateHandler local PlayerUpdateHandler = ____exports.PlayerUpdateHandler
PlayerUpdateHandler.name = "PlayerUpdateHandler" PlayerUpdateHandler.name = "PlayerUpdateHandler"
@ -31,7 +31,7 @@ function PlayerUpdateHandler.prototype.playerUpdateThrottle(self, event, mutate,
return return
end end
local eventName = Converters.eventNameString(event.name) local eventName = Converters.eventNameString(event.name)
EventUpdatesManager:throttle( EventUpdates:throttle(
{index = playerIndex}, {index = playerIndex},
KafkatorioPacketData.Type.PlayerUpdate, KafkatorioPacketData.Type.PlayerUpdate,
function(data) function(data)

View file

@ -4,7 +4,7 @@ local ____converters = require("main.events.converters")
local Converters = ____converters.Converters local Converters = ____converters.Converters
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____PacketEmitter = require("main.PacketEmitter") local ____PacketEmitter = require("main.emitting.PacketEmitter")
local packetEmitter = ____PacketEmitter.default local packetEmitter = ____PacketEmitter.default
local ____PlayerUpdateHandler = require("main.events.handlers.PlayerUpdateHandler") local ____PlayerUpdateHandler = require("main.events.handlers.PlayerUpdateHandler")
local PlayerUpdates = ____PlayerUpdateHandler.default local PlayerUpdates = ____PlayerUpdateHandler.default

View file

@ -1,25 +1,21 @@
local ____exports = {} local ____exports = {}
local emit, getMapTilePrototypes, getEntityPrototypes, convertItemGroup, convertItemSubgroup local emit, getMapTilePrototypes, getEntityPrototypes, convertItemGroup, convertItemSubgroup
local ____PacketEmitter = require("main.PacketEmitter")
local PacketEmitter = ____PacketEmitter.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local FactorioPrototype = ____kafkatorio_2Dschema.FactorioPrototype local FactorioPrototype = ____kafkatorio_2Dschema.FactorioPrototype
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
local ____converters = require("main.events.converters") local ____converters = require("main.events.converters")
local Converters = ____converters.Converters local Converters = ____converters.Converters
local ____KafkatorioPacketQueue = require("main.emitting.KafkatorioPacketQueue")
local KafkatorioPacketQueue = ____KafkatorioPacketQueue.default
function emit(protosByType) function emit(protosByType)
for ____, protos in pairs(protosByType) do for ____, protos in pairs(protosByType) do
PacketEmitter:emitInstantPacket({type = KafkatorioPacketData.Type.PrototypesUpdate, prototypes = protos}) KafkatorioPacketQueue:enqueue({type = KafkatorioPacketData.Type.PrototypesUpdate, prototypes = protos})
end end
end end
function getMapTilePrototypes() function getMapTilePrototypes()
local tiles = {} local tiles = {}
if not (tiles.tile ~= nil) then
tiles.tile = {}
end
for ____, tile in pairs(game.tile_prototypes) do for ____, tile in pairs(game.tile_prototypes) do
local ____tiles_tile_0 = tiles.tile tiles[#tiles + 1] = {
____tiles_tile_0[#____tiles_tile_0 + 1] = {
type = FactorioPrototype.Type.MapTile, type = FactorioPrototype.Type.MapTile,
protoId = Converters.prototypeId("tile", tile.name), protoId = Converters.prototypeId("tile", tile.name),
order = tile.order, order = tile.order,
@ -29,13 +25,13 @@ function getMapTilePrototypes()
canBeMined = tile.mineable_properties.minable canBeMined = tile.mineable_properties.minable
} }
end end
return tiles return {tiles = tiles}
end end
function getEntityPrototypes() function getEntityPrototypes()
local protos = {} local protos = {}
for ____, entity in pairs(game.entity_prototypes) do for ____, entity in pairs(game.entity_prototypes) do
local key = (entity.group.name .. "/") .. entity.subgroup.name local key = (entity.group.name .. "/") .. entity.subgroup.name
if not (protos[key] ~= nil) then if protos[key] == nil then
protos[key] = {} protos[key] = {}
end end
local entityProto = { local entityProto = {
@ -58,8 +54,8 @@ function getEntityPrototypes()
entityProto.mapColourEnemy = Converters.mapColour(entity.enemy_map_color) entityProto.mapColourEnemy = Converters.mapColour(entity.enemy_map_color)
end end
entityProto.miningProperties = Converters.miningProperties(entity.mineable_properties) entityProto.miningProperties = Converters.miningProperties(entity.mineable_properties)
local ____protos_key_1 = protos[key] local ____protos_key_0 = protos[key]
____protos_key_1[#____protos_key_1 + 1] = entityProto ____protos_key_0[#____protos_key_0 + 1] = entityProto
end end
return protos return protos
end end
@ -67,19 +63,17 @@ function convertItemGroup(itemGroup)
return {name = itemGroup.name, type = itemGroup.type, parentName = nil} return {name = itemGroup.name, type = itemGroup.type, parentName = nil}
end end
function convertItemSubgroup(itemGroup) function convertItemSubgroup(itemGroup)
local ____itemGroup_name_4 = itemGroup.name local ____itemGroup_name_3 = itemGroup.name
local ____itemGroup_type_5 = itemGroup.type local ____itemGroup_type_4 = itemGroup.type
local ____itemGroup_group_name_2 = itemGroup.group local ____itemGroup_group_name_1 = itemGroup.group
if ____itemGroup_group_name_2 ~= nil then if ____itemGroup_group_name_1 ~= nil then
____itemGroup_group_name_2 = ____itemGroup_group_name_2.name ____itemGroup_group_name_1 = ____itemGroup_group_name_1.name
end end
return {name = ____itemGroup_name_4, type = ____itemGroup_type_5, parentName = ____itemGroup_group_name_2} return {name = ____itemGroup_name_3, type = ____itemGroup_type_4, parentName = ____itemGroup_group_name_1}
end end
function ____exports.emitPrototypes() function ____exports.emitPrototypes()
local mapTileProtos = getMapTilePrototypes() local mapTileProtos = getMapTilePrototypes()
emit(mapTileProtos) emit(mapTileProtos)
local ____ = game.item_group_prototypes
local ____ = game.item_subgroup_prototypes
local entityProtos = getEntityPrototypes() local entityProtos = getEntityPrototypes()
emit(entityProtos) emit(entityProtos)
end end

View file

@ -1,16 +1,20 @@
local ____exports = {} local ____exports = {}
local ____queue = require("main.queue.queue")
local Queue = ____queue.Queue
local ____eventTypeCheck = require("main.events.eventTypeCheck") local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType local isEventType = ____eventTypeCheck.isEventType
local ____converters = require("main.events.converters") local ____converters = require("main.events.converters")
local Converters = ____converters.Converters local Converters = ____converters.Converters
local ____surfaceUpdates = require("main.events.surfaceUpdates") local ____surfaceUpdates = require("main.events.surfaceUpdates")
local handleSurfaceUpdate = ____surfaceUpdates.handleSurfaceUpdate local handleSurfaceUpdate = ____surfaceUpdates.handleSurfaceUpdate
local ____EventDataCache = require("main.cache.EventDataCache") local ____EventDataCache = require("main.emitting.EventDataCache")
local EventCacheService = ____EventDataCache.default local EventCacheService = ____EventDataCache.default
local ____MapChunkUpdateHandler = require("main.events.handlers.MapChunkUpdateHandler") local ____MapChunkUpdateHandler = require("main.events.handlers.MapChunkUpdateHandler")
local MapChunkUpdate = ____MapChunkUpdateHandler.default local MapChunkUpdate = ____MapChunkUpdateHandler.default
local ____queue = require("main.queue.queue")
local EventDataQueue = ____queue.default
local ____KafkatorioPacketQueue = require("main.emitting.KafkatorioPacketQueue")
local KafkatorioPacketQueue = ____KafkatorioPacketQueue.default
local ____PacketEmitter = require("main.emitting.PacketEmitter")
local PacketEmitter = ____PacketEmitter.default
script.on_event( script.on_event(
defines.events.on_tick, defines.events.on_tick,
function(event) function(event)
@ -23,6 +27,10 @@ script.on_event(
) )
end end
end end
local kPacket = KafkatorioPacketQueue:dequeue()
if kPacket ~= nil then
PacketEmitter:emitInstantPacket(kPacket)
end
if event.tick % 6 == 0 then if event.tick % 6 == 0 then
local emittedCount = EventCacheService:extractAndEmitExpiredPackets() local emittedCount = EventCacheService:extractAndEmitExpiredPackets()
if emittedCount > 0 then if emittedCount > 0 then
@ -30,9 +38,9 @@ script.on_event(
end end
end end
if event.tick % 7 == 0 then if event.tick % 7 == 0 then
local events = Queue.dequeueValues(1) local events = EventDataQueue:dequeueValues(1)
if #events > 0 then if #events > 0 then
log((((("[on_tick:" .. tostring(event.tick)) .. "] dequeued ") .. tostring(#events)) .. " events, current size: ") .. tostring(Queue.size())) log((((("[on_tick:" .. tostring(event.tick)) .. "] dequeued ") .. tostring(#events)) .. " events, current size: ") .. tostring(EventDataQueue:size()))
local i = 1 local i = 1
for ____, event in ipairs(events) do for ____, event in ipairs(events) do
if isEventType(event, defines.events.on_chunk_generated) then if isEventType(event, defines.events.on_chunk_generated) then

View file

@ -1,5 +1,5 @@
local ____exports = {} local ____exports = {}
local ____PacketEmitter = require("main.PacketEmitter") local ____PacketEmitter = require("main.emitting.PacketEmitter")
local PacketEmitter = ____PacketEmitter.default local PacketEmitter = ____PacketEmitter.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema") local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData

View file

@ -1,8 +1,8 @@
local ____exports = {} local ____exports = {}
local ____EventDataCache = require("main.emitting.EventDataCache")
local EventUpdates = ____EventDataCache.default
local ____queue = require("main.queue.queue") local ____queue = require("main.queue.queue")
local Queue = ____queue.Queue local EventDataQueueManager = ____queue.EventDataQueueManager
local ____EventDataCache = require("main.cache.EventDataCache")
local EventUpdatesManager = ____EventDataCache.default
function ____exports.initGlobal(force) function ____exports.initGlobal(force)
if force == nil then if force == nil then
force = false force = false
@ -10,8 +10,8 @@ function ____exports.initGlobal(force)
log(("Initialising Kafkatorio Global variables (force=" .. tostring(force)) .. ")...") log(("Initialising Kafkatorio Global variables (force=" .. tostring(force)) .. ")...")
global.MOD_VERSION = script.active_mods[script.mod_name] global.MOD_VERSION = script.active_mods[script.mod_name]
global.FACTORIO_VERSION = script.active_mods.base global.FACTORIO_VERSION = script.active_mods.base
Queue.init(true) EventDataQueueManager:init(true)
EventUpdatesManager:init(true) EventUpdates:init(true)
log("Finished initialising Kafkatorio Global variables") log("Finished initialising Kafkatorio Global variables")
end end
return ____exports return ____exports

View file

@ -1,26 +1,28 @@
local ____lualib = require("lualib_bundle") local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local __TS__Class = ____lualib.__TS__Class local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local ____exports = {} local ____exports = {}
____exports.Queue = {} local Node
local Queue = ____exports.Queue ____exports.EventDataQueueManager = __TS__Class()
do local EventDataQueueManager = ____exports.EventDataQueueManager
local Node EventDataQueueManager.name = "EventDataQueueManager"
function Queue.init(force) function EventDataQueueManager.prototype.____constructor(self)
end
function EventDataQueueManager.init(self, force)
local isAnythingUndefined = global.store == nil or global.size == nil or global.head == nil or global.tail == nil local isAnythingUndefined = global.store == nil or global.size == nil or global.head == nil or global.tail == nil
log(((("Initialising EventQueue globals (force=" .. tostring(force)) .. ", isAnythingUndefined=") .. tostring(isAnythingUndefined)) .. ")") log(((("Initialising EventDataQueueManager globals (force=" .. tostring(force)) .. ", isAnythingUndefined=") .. tostring(isAnythingUndefined)) .. ")")
if force == true or isAnythingUndefined then if force == true or isAnythingUndefined then
global.store = {} global.store = {}
global.head = nil global.head = nil
global.tail = nil global.tail = nil
global.size = 0 global.size = 0
end end
log("Finished initialising EventQueue") log("Finished initialising EventDataQueueManager")
end end
function Queue.size() function EventDataQueueManager.prototype.size(self)
return global.size return global.size
end end
function Queue.enqueue(key, event, weight) function EventDataQueueManager.prototype.enqueue(self, key, event, weight)
if weight == nil then if weight == nil then
weight = 1 weight = 1
end end
@ -36,8 +38,8 @@ do
global.size = global.size + 1 global.size = global.size + 1
end end
global.store[key] = event global.store[key] = event
end end
function Queue.dequeue() function EventDataQueueManager.prototype.dequeue(self)
if global.head == nil then if global.head == nil then
return nil return nil
else else
@ -53,8 +55,8 @@ do
return {value = storedPacket, weight = weight} return {value = storedPacket, weight = weight}
end end
end end
end end
function Queue.dequeueValues(targetWeight) function EventDataQueueManager.prototype.dequeueValues(self, targetWeight)
if targetWeight == nil then if targetWeight == nil then
targetWeight = 50 targetWeight = 50
end end
@ -62,7 +64,7 @@ do
local weight = 0 local weight = 0
repeat repeat
do do
local packet = Queue.dequeue() local packet = self:dequeue()
if packet == nil then if packet == nil then
break break
else else
@ -72,13 +74,14 @@ do
end end
until not (weight <= targetWeight) until not (weight <= targetWeight)
return values return values
end end
Node = __TS__Class() local EventDataQueue = __TS__New(____exports.EventDataQueueManager)
Node.name = "Node" ____exports.default = EventDataQueue
function Node.prototype.____constructor(self, storeKey, weight) Node = __TS__Class()
Node.name = "Node"
function Node.prototype.____constructor(self, storeKey, weight)
self.next = nil self.next = nil
self.storeKey = storeKey self.storeKey = storeKey
self.weight = weight self.weight = weight
end
end end
return ____exports return ____exports

View file

@ -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

View file

@ -1,6 +1,6 @@
import {initGlobal} from "../global-init"; import {initGlobal} from "../global-init";
import {Queue} from "../queue/queue";
import {emitPrototypes} from "../events/prototypeUpdates"; import {emitPrototypes} from "../events/prototypeUpdates";
import EventDataQueue from "../queue/queue";
commands.add_command( commands.add_command(
@ -58,7 +58,7 @@ commands.add_command(
tick: e.tick, tick: e.tick,
} }
Queue.enqueue( EventDataQueue.enqueue(
`${surface.index}${chunk.x}${chunk.y}`, `${surface.index}${chunk.x}${chunk.y}`,
data, data,
50 50

View file

@ -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
}
}

View file

@ -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>
}

View file

@ -1,8 +1,5 @@
import { import {KafkatorioPacket, KafkatorioPacketData} from "../../generated/kafkatorio-schema";
KafkatorioPacket, import {KafkatorioKeyedPacketData} from "../types";
KafkatorioPacketData
} from "../generated/kafkatorio-schema";
import {KafkatorioKeyedPacketData} from "./types";
export class KafkatorioPacketEmitter { export class KafkatorioPacketEmitter {

View file

@ -1,33 +1,26 @@
import PacketEmitter from "../PacketEmitter";
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema"; import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
import Type = KafkatorioPacketData.Type; import KafkatorioPacketQueue from "../emitting/KafkatorioPacketQueue";
script.on_event( script.on_event(defines.events.on_console_chat, (event: OnConsoleChatEvent) => {
defines.events.on_console_chat,
(event: OnConsoleChatEvent) => {
const update: KafkatorioPacketData.ConsoleChatUpdate = { const update: KafkatorioPacketData.ConsoleChatUpdate = {
type: Type.ConsoleChatUpdate, type: KafkatorioPacketData.Type.ConsoleChatUpdate,
authorPlayerIndex: event.player_index ?? null, authorPlayerIndex: event.player_index ?? null,
content: event.message content: event.message
} }
PacketEmitter.emitInstantPacket(update) KafkatorioPacketQueue.enqueue(update)
} })
)
script.on_event( script.on_event(defines.events.on_console_command, (event: OnConsoleCommandEvent) => {
defines.events.on_console_command,
(event: OnConsoleCommandEvent) => {
const update: KafkatorioPacketData.ConsoleCommandUpdate = { const update: KafkatorioPacketData.ConsoleCommandUpdate = {
type: Type.ConsoleCommandUpdate, type: KafkatorioPacketData.Type.ConsoleCommandUpdate,
authorPlayerIndex: event.player_index ?? null, authorPlayerIndex: event.player_index ?? null,
command: event.command, command: event.command,
parameters: event.parameters, parameters: event.parameters,
} }
PacketEmitter.emitInstantPacket(update) KafkatorioPacketQueue.enqueue(update)
} })
)

View file

@ -3,8 +3,9 @@ import {
ConfigurationUpdateModData, ConfigurationUpdateModData,
KafkatorioPacketData KafkatorioPacketData
} from "../../../generated/kafkatorio-schema"; } from "../../../generated/kafkatorio-schema";
import PacketEmitter from "../../PacketEmitter"; import PacketEmitter from "../../emitting/PacketEmitter";
import ConfigurationUpdate = KafkatorioPacketData.ConfigurationUpdate; import ConfigurationUpdate = KafkatorioPacketData.ConfigurationUpdate;
import KafkatorioPacketQueue from "../../emitting/KafkatorioPacketQueue";
export class ConfigurationUpdateHandler { export class ConfigurationUpdateHandler {
@ -20,7 +21,7 @@ export class ConfigurationUpdateHandler {
factorioData: ConfigurationUpdateHandler.factorioData(changeData) factorioData: ConfigurationUpdateHandler.factorioData(changeData)
} }
PacketEmitter.emitInstantPacket(configUpdateData) KafkatorioPacketQueue.enqueue(configUpdateData)
} }

View file

@ -6,7 +6,7 @@ import {
Tick Tick
} from "../../../generated/kafkatorio-schema"; } from "../../../generated/kafkatorio-schema";
import {Converters} from "../converters"; import {Converters} from "../converters";
import EventDataCache from "../../cache/EventDataCache"; import EventDataCache from "../../emitting/EventDataCache";
export class EntityUpdatesHandler { export class EntityUpdatesHandler {

View file

@ -3,13 +3,11 @@ import {isEventType} from "../eventTypeCheck";
import { import {
KafkatorioPacketData, KafkatorioPacketData,
MapChunkPosition, MapChunkPosition,
MinedProduct,
PrototypeKey PrototypeKey
} from "../../../generated/kafkatorio-schema"; } from "../../../generated/kafkatorio-schema";
import EventUpdatesManager, {EventUpdates} from "../../cache/EventDataCache"; import EventUpdates, {PacketKey} from "../../emitting/EventDataCache";
import {MapChunkUpdateEvent, MapTileChangeEvent} from "../mapChunkUpdates"; import {MapChunkUpdateEvent, MapTileChangeEvent} from "../mapChunkUpdates";
import PacketKey = EventUpdates.PacketKey;
import Type = MinedProduct.Type;
type MapChunkTileUpdater = (data: KafkatorioPacketData.MapChunkTileUpdate) => void type MapChunkTileUpdater = (data: KafkatorioPacketData.MapChunkTileUpdate) => void
@ -141,7 +139,7 @@ class MapChunkUpdateHandler {
chunkPosition: [position.x, position.y], chunkPosition: [position.x, position.y],
} }
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>( EventUpdates.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key, key,
KafkatorioPacketData.Type.MapChunkTileUpdate, KafkatorioPacketData.Type.MapChunkTileUpdate,
data => data.isDeleted = true, data => data.isDeleted = true,
@ -171,7 +169,7 @@ class MapChunkUpdateHandler {
chunkPosition: chunkPosition, chunkPosition: chunkPosition,
} }
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>( EventUpdates.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key, key,
KafkatorioPacketData.Type.MapChunkTileUpdate, KafkatorioPacketData.Type.MapChunkTileUpdate,
data => { data => {

View file

@ -1,6 +1,6 @@
import {KafkatorioPacketData} from "../../../generated/kafkatorio-schema"; import {KafkatorioPacketData} from "../../../generated/kafkatorio-schema";
import {Converters} from "../converters"; import {Converters} from "../converters";
import EventUpdatesManager from "../../cache/EventDataCache"; import EventUpdates from "../../emitting/EventDataCache";
export type PlayerUpdater = (player: LuaPlayer, data: KafkatorioPacketData.PlayerUpdate) => void export type PlayerUpdater = (player: LuaPlayer, data: KafkatorioPacketData.PlayerUpdate) => void
@ -43,7 +43,7 @@ export class PlayerUpdateHandler {
const eventName = Converters.eventNameString(event.name) const eventName = Converters.eventNameString(event.name)
EventUpdatesManager.throttle<KafkatorioPacketData.PlayerUpdate>( EventUpdates.throttle<KafkatorioPacketData.PlayerUpdate>(
{index: playerIndex}, {index: playerIndex},
KafkatorioPacketData.Type.PlayerUpdate, KafkatorioPacketData.Type.PlayerUpdate,
data => { data => {

View file

@ -1,6 +1,6 @@
import {Converters} from "./converters"; import {Converters} from "./converters";
import {ForceIndex, KafkatorioPacketData, PlayerUpdateKey} from "../../generated/kafkatorio-schema"; import {ForceIndex, KafkatorioPacketData, PlayerUpdateKey} from "../../generated/kafkatorio-schema";
import packetEmitter from "../PacketEmitter"; import packetEmitter from "../emitting/PacketEmitter";
import PlayerUpdates from "./handlers/PlayerUpdateHandler"; import PlayerUpdates from "./handlers/PlayerUpdateHandler";

View file

@ -1,13 +1,14 @@
import PacketEmitter from "../PacketEmitter"; import PacketEmitter from "../emitting/PacketEmitter";
import { import {
EntityItemGroup, EntityItemGroup,
FactorioPrototype, FactorioPrototype,
KafkatorioPacketData KafkatorioPacketData
} from "../../generated/kafkatorio-schema"; } from "../../generated/kafkatorio-schema";
import {Converters} from "./converters"; 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() { export function emitPrototypes() {
@ -16,10 +17,6 @@ export function emitPrototypes() {
emit(mapTileProtos) emit(mapTileProtos)
game.item_group_prototypes
game.item_subgroup_prototypes
const entityProtos: PrototypesByType<FactorioPrototype.Entity> = getEntityPrototypes() const entityProtos: PrototypesByType<FactorioPrototype.Entity> = getEntityPrototypes()
emit(entityProtos) emit(entityProtos)
} }
@ -27,7 +24,7 @@ export function emitPrototypes() {
function emit<T extends FactorioPrototype>(protosByType: PrototypesByType<T>) { function emit<T extends FactorioPrototype>(protosByType: PrototypesByType<T>) {
for (const [, protos] of pairs(protosByType)) { for (const [, protos] of pairs(protosByType)) {
PacketEmitter.emitInstantPacket({ KafkatorioPacketQueue.enqueue({
type: KafkatorioPacketData.Type.PrototypesUpdate, type: KafkatorioPacketData.Type.PrototypesUpdate,
prototypes: protos, prototypes: protos,
}) })
@ -36,13 +33,10 @@ function emit<T extends FactorioPrototype>(protosByType: PrototypesByType<T>) {
function getMapTilePrototypes(): PrototypesByType<FactorioPrototype.MapTile> { function getMapTilePrototypes(): PrototypesByType<FactorioPrototype.MapTile> {
const tiles = new LuaTable<string, FactorioPrototype.MapTile[]>() const tiles: FactorioPrototype.MapTile[] = []
if (!tiles.has("tile")) {
tiles.set("tile", [])
}
for (const [, tile] of game.tile_prototypes) { for (const [, tile] of game.tile_prototypes) {
tiles.get("tile").push({ tiles[tiles.length] = {
type: FactorioPrototype.Type.MapTile, type: FactorioPrototype.Type.MapTile,
protoId: Converters.prototypeId("tile", tile.name), protoId: Converters.prototypeId("tile", tile.name),
@ -51,21 +45,22 @@ function getMapTilePrototypes(): PrototypesByType<FactorioPrototype.MapTile> {
collisionMasks: Converters.convertCollisionMaskToNames(tile.collision_mask), collisionMasks: Converters.convertCollisionMaskToNames(tile.collision_mask),
mapColour: Converters.mapColour(tile.map_color), mapColour: Converters.mapColour(tile.map_color),
canBeMined: tile.mineable_properties.minable, canBeMined: tile.mineable_properties.minable,
})
} }
return tiles }
return {
"tiles": tiles
}
} }
function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> { function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
const protos = new LuaTable<string, FactorioPrototype.Entity[]>() const protos: PrototypesByType<FactorioPrototype.Entity> = {}
for (const [, entity] of game.entity_prototypes) { for (const [, entity] of game.entity_prototypes) {
const key: string = `${entity.group.name}/${entity.subgroup.name}` const key: string = `${entity.group.name}/${entity.subgroup.name}`
if (!protos.has(key)) { protos[key] ??= []
protos.set(key, [])
}
const entityProto: FactorioPrototype.Entity = { const entityProto: FactorioPrototype.Entity = {
type: FactorioPrototype.Type.Entity, type: FactorioPrototype.Type.Entity,
@ -91,7 +86,7 @@ function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
} }
entityProto.miningProperties = Converters.miningProperties(entity.mineable_properties) entityProto.miningProperties = Converters.miningProperties(entity.mineable_properties)
protos.get(key).push(entityProto) protos[key].push(entityProto)
} }
return protos return protos
} }

View file

@ -1,20 +1,26 @@
import {Queue} from "../queue/queue";
import {isEventType} from "./eventTypeCheck"; import {isEventType} from "./eventTypeCheck";
import {Converters} from "./converters"; import {Converters} from "./converters";
import {handleSurfaceUpdate} from "./surfaceUpdates"; import {handleSurfaceUpdate} from "./surfaceUpdates";
import EventCacheService from "../cache/EventDataCache"; import EventCacheService from "../emitting/EventDataCache";
import MapChunkUpdate from "./handlers/MapChunkUpdateHandler"; 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( script.on_event(defines.events.on_tick, (event: OnTickEvent) => {
defines.events.on_tick,
(event: OnTickEvent) => {
if (event.tick % 1000 == 0) { if (event.tick % 1000 == 0) {
for (const [, surface] of game.surfaces) { for (const [, surface] of game.surfaces) {
handleSurfaceUpdate(event, Converters.eventNameString(event.name), surface) handleSurfaceUpdate(event, Converters.eventNameString(event.name), surface)
} }
} }
const kPacket: KafkatorioPacketData | null = KafkatorioPacketQueue.dequeue()
if (kPacket != null) {
PacketEmitter.emitInstantPacket(kPacket)
}
if (event.tick % 6 == 0) { if (event.tick % 6 == 0) {
// Every 0.1 seconds // Every 0.1 seconds
const emittedCount = EventCacheService.extractAndEmitExpiredPackets() const emittedCount = EventCacheService.extractAndEmitExpiredPackets()
@ -24,10 +30,10 @@ script.on_event(
} }
if (event.tick % 7 == 0) { if (event.tick % 7 == 0) {
const events: EventData[] = Queue.dequeueValues(1) const events: EventData[] = EventDataQueue.dequeueValues(1)
if (events.length > 0) { if (events.length > 0) {
log(`[on_tick:${event.tick}] dequeued ${events.length} events, current size: ${Queue.size()}`) log(`[on_tick:${event.tick}] dequeued ${events.length} events, current size: ${EventDataQueue.size()}`)
let i = 1 let i = 1
for (const event of events) { for (const event of events) {
@ -41,5 +47,4 @@ script.on_event(
} }
} }
} }
} })
)

View file

@ -1,7 +1,4 @@
script.on_event( script.on_event(defines.events.on_runtime_mod_setting_changed, (e: OnRuntimeModSettingChangedEvent) => {
defines.events.on_runtime_mod_setting_changed,
(e: OnRuntimeModSettingChangedEvent) => {
print(`Settings changed ${e.setting_type}, ${e.player_index}, ${e.setting}, ${e.mod_name}`) print(`Settings changed ${e.setting_type}, ${e.player_index}, ${e.setting}, ${e.mod_name}`)
// KafkatorioSettings.loadSettings() // KafkatorioSettings.loadSettings()
} })
)

View file

@ -1,4 +1,4 @@
import PacketEmitter from "../PacketEmitter"; import PacketEmitter from "../emitting/PacketEmitter";
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema"; import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
import Type = KafkatorioPacketData.Type; import Type = KafkatorioPacketData.Type;

View file

@ -1,5 +1,5 @@
import {Queue} from "./queue/queue"; import EventUpdates from "./emitting/EventDataCache";
import EventUpdatesManager from "./cache/EventDataCache"; import {EventDataQueueManager} from "./queue/queue";
export function initGlobal(force: boolean = false) { 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.MOD_VERSION = script.active_mods[script.mod_name]
global.FACTORIO_VERSION = script.active_mods["base"] global.FACTORIO_VERSION = script.active_mods["base"]
Queue.init(true) EventDataQueueManager.init(true)
EventUpdatesManager.init(true) EventUpdates.init(true)
log("Finished initialising Kafkatorio Global variables") log("Finished initialising Kafkatorio Global variables")
} }

View file

@ -1,23 +1,13 @@
export namespace Queue { export class EventDataQueueManager {
declare const global: { static init(force?: boolean) {
store: EventQueueStore
head: Node | null
tail: Node | null
size: uint
}
export function init(force?: boolean) {
const isAnythingUndefined = global.store == undefined || const isAnythingUndefined = global.store == undefined ||
global.size == undefined || global.size == undefined ||
global.head == undefined || global.head == undefined ||
global.tail == undefined global.tail == undefined
log(`Initialising EventQueue globals (force=${force}, isAnythingUndefined=${isAnythingUndefined})`) log(`Initialising EventDataQueueManager globals (force=${force}, isAnythingUndefined=${isAnythingUndefined})`)
if (force == true || isAnythingUndefined) { if (force == true || isAnythingUndefined) {
global.store = {} global.store = {}
@ -26,16 +16,16 @@ export namespace Queue {
global.size = 0 global.size = 0
} }
log(`Finished initialising EventQueue`) log(`Finished initialising EventDataQueueManager`)
} }
export function size(): uint | undefined { size(): uint | undefined {
return global.size 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) { if (key ! in global.store) {
let node = new Node(key, weight) let node = new Node(key, weight)
if (global.head == null) { if (global.head == null) {
@ -52,7 +42,7 @@ export namespace Queue {
} }
export function dequeue(): WeightedPacket | null { dequeue(): WeightedPacket | null {
if (global.head == null) { if (global.head == null) {
return null return null
} else { } else {
@ -76,12 +66,12 @@ export namespace Queue {
} }
export function dequeueValues(targetWeight: uint = 50): EventData[] { dequeueValues(targetWeight: uint = 50): EventData[] {
let values: EventData[] = [] let values: EventData[] = []
let weight: uint = 0 let weight: uint = 0
do { do {
let packet = dequeue() let packet = this.dequeue()
if (packet == null) { if (packet == null) {
break; break;
@ -94,8 +84,26 @@ export namespace Queue {
return values return values
} }
}
class Node {
const EventDataQueue = new EventDataQueueManager()
export default EventDataQueue
declare const global: {
store: EventQueueStore
head: Node | null
tail: Node | null
size: uint
}
class Node {
storeKey: string; storeKey: string;
next: Node | null = null; next: Node | null = null;
weight: uint weight: uint
@ -104,17 +112,15 @@ export namespace Queue {
this.storeKey = storeKey; this.storeKey = storeKey;
this.weight = weight this.weight = weight
} }
} }
interface EventQueueStore { interface EventQueueStore {
[key: string]: EventData | null; [key: string]: EventData | null;
} }
interface WeightedPacket { interface WeightedPacket {
value: EventData, value: EventData,
weight: int, weight: int,
}
} }