massive refactor to export entities (buildings and resources)

- minor version bumps
- new Factorio version
- use both prototype name and type to identify a prototype
- kxs-ts-gen: use BrandTyping, but exclude some identifiers
- kxs-ts-gen: manually create zip dist, so it can run after the project compiles
- mod: use typed-factorio strict types
- mod: refactor event handlers to be a little more consistent and object-oriented
This commit is contained in:
Adam 2022-07-05 23:44:23 +02:00
parent 88982d818a
commit 90dc520d22
62 changed files with 2629 additions and 713 deletions

View file

@ -3,7 +3,7 @@ local ____global_2Dinit = require("main.global-init")
local initGlobal = ____global_2Dinit.initGlobal
local ____prototypeUpdates = require("main.events.prototypeUpdates")
local emitPrototypes = ____prototypeUpdates.emitPrototypes
require("main.events.registerHandlers")
require("main.events.registerEventListeners")
require("main.events.mapChunkUpdates")
require("main.events.playerUpdates")
require("main.events.entityUpdates")

View file

@ -9,7 +9,8 @@ do
KafkatorioPacketData.Type.PrototypesUpdate = "kafkatorio.packet.instant.PrototypesUpdate"
KafkatorioPacketData.Type.SurfaceUpdate = "kafkatorio.packet.instant.SurfaceUpdate"
KafkatorioPacketData.Type.EntityUpdate = "kafkatorio.packet.keyed.EntityUpdate"
KafkatorioPacketData.Type.MapChunkUpdate = "kafkatorio.packet.keyed.MapChunkUpdate"
KafkatorioPacketData.Type.MapChunkEntityUpdate = "kafkatorio.packet.keyed.MapChunkEntityUpdate"
KafkatorioPacketData.Type.MapChunkTileUpdate = "kafkatorio.packet.keyed.MapChunkTileUpdate"
KafkatorioPacketData.Type.PlayerUpdate = "kafkatorio.packet.keyed.PlayerUpdate"
KafkatorioPacketData.Type.Error = "kafkatorio.packet.KafkatorioPacketData.Error"
end
@ -20,7 +21,62 @@ do
FactorioPrototype.Type.Entity = "kafkatorio.prototype.Entity"
FactorioPrototype.Type.MapTile = "kafkatorio.prototype.MapTile"
end
____exports.MinedProductType = MinedProductType or ({})
____exports.MinedProductType.item = "item"
____exports.MinedProductType.fluid = "fluid"
____exports.FactorioEntityData = {}
local FactorioEntityData = ____exports.FactorioEntityData
do
FactorioEntityData.Type = Type or ({})
FactorioEntityData.Type.Resource = "kafkatorio.entity.FactorioEntityData.Resource"
FactorioEntityData.Type.Standard = "kafkatorio.entity.FactorioEntityData.Standard"
end
____exports.EntityStatus = EntityStatus or ({})
____exports.EntityStatus.CANT_DIVIDE_SEGMENTS = "CANT_DIVIDE_SEGMENTS"
____exports.EntityStatus.CHARGING = "CHARGING"
____exports.EntityStatus.CLOSED_BY_CIRCUIT_NETWORK = "CLOSED_BY_CIRCUIT_NETWORK"
____exports.EntityStatus.DISABLED = "DISABLED"
____exports.EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR = "DISABLED_BY_CONTROL_BEHAVIOR"
____exports.EntityStatus.DISABLED_BY_SCRIPT = "DISABLED_BY_SCRIPT"
____exports.EntityStatus.DISCHARGING = "DISCHARGING"
____exports.EntityStatus.FLUID_INGREDIENT_SHORTAGE = "FLUID_INGREDIENT_SHORTAGE"
____exports.EntityStatus.FULLY_CHARGED = "FULLY_CHARGED"
____exports.EntityStatus.FULL_OUTPUT = "FULL_OUTPUT"
____exports.EntityStatus.ITEM_INGREDIENT_SHORTAGE = "ITEM_INGREDIENT_SHORTAGE"
____exports.EntityStatus.LAUNCHING_ROCKET = "LAUNCHING_ROCKET"
____exports.EntityStatus.LOW_INPUT_FLUID = "LOW_INPUT_FLUID"
____exports.EntityStatus.LOW_POWER = "LOW_POWER"
____exports.EntityStatus.LOW_TEMPERATURE = "LOW_TEMPERATURE"
____exports.EntityStatus.MARKED_FOR_DECONSTRUCTION = "MARKED_FOR_DECONSTRUCTION"
____exports.EntityStatus.MISSING_REQUIRED_FLUID = "MISSING_REQUIRED_FLUID"
____exports.EntityStatus.MISSING_SCIENCE_PACKS = "MISSING_SCIENCE_PACKS"
____exports.EntityStatus.NETWORKS_CONNECTED = "NETWORKS_CONNECTED"
____exports.EntityStatus.NETWORKS_DISCONNECTED = "NETWORKS_DISCONNECTED"
____exports.EntityStatus.NORMAL = "NORMAL"
____exports.EntityStatus.NOT_CONNECTED_TO_RAIL = "NOT_CONNECTED_TO_RAIL"
____exports.EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK = "NOT_PLUGGED_IN_ELECTRIC_NETWORK"
____exports.EntityStatus.NO_AMMO = "NO_AMMO"
____exports.EntityStatus.NO_FUEL = "NO_FUEL"
____exports.EntityStatus.NO_INGREDIENTS = "NO_INGREDIENTS"
____exports.EntityStatus.NO_INPUT_FLUID = "NO_INPUT_FLUID"
____exports.EntityStatus.NO_MINABLE_RESOURCES = "NO_MINABLE_RESOURCES"
____exports.EntityStatus.NO_MODULES_TO_TRANSMIT = "NO_MODULES_TO_TRANSMIT"
____exports.EntityStatus.NO_POWER = "NO_POWER"
____exports.EntityStatus.NO_RECIPE = "NO_RECIPE"
____exports.EntityStatus.NO_RESEARCH_IN_PROGRESS = "NO_RESEARCH_IN_PROGRESS"
____exports.EntityStatus.OPENED_BY_CIRCUIT_NETWORK = "OPENED_BY_CIRCUIT_NETWORK"
____exports.EntityStatus.OUT_OF_LOGISTIC_NETWORK = "OUT_OF_LOGISTIC_NETWORK"
____exports.EntityStatus.PREPARING_ROCKET_FOR_LAUNCH = "PREPARING_ROCKET_FOR_LAUNCH"
____exports.EntityStatus.RECHARGING_AFTER_POWER_OUTAGE = "RECHARGING_AFTER_POWER_OUTAGE"
____exports.EntityStatus.TURNED_OFF_DURING_DAYTIME = "TURNED_OFF_DURING_DAYTIME"
____exports.EntityStatus.WAITING_FOR_SOURCE_ITEMS = "WAITING_FOR_SOURCE_ITEMS"
____exports.EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION = "WAITING_FOR_SPACE_IN_DESTINATION"
____exports.EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT = "WAITING_FOR_TARGET_TO_BE_BUILT"
____exports.EntityStatus.WAITING_FOR_TRAIN = "WAITING_FOR_TRAIN"
____exports.EntityStatus.WAITING_TO_LAUNCH_ROCKET = "WAITING_TO_LAUNCH_ROCKET"
____exports.EntityStatus.WORKING = "WORKING"
____exports.MinedProduct = {}
local MinedProduct = ____exports.MinedProduct
do
MinedProduct.Type = Type or ({})
MinedProduct.Type.MinedProductFluid = "kafkatorio.resource.MinedProduct.MinedProductFluid"
MinedProduct.Type.MinedProductItem = "kafkatorio.resource.MinedProduct.MinedProductItem"
end
return ____exports

View file

@ -1,7 +1,22 @@
local ____exports = {}
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local EntityStatus = ____kafkatorio_2Dschema.EntityStatus
local FactorioEntityData = ____kafkatorio_2Dschema.FactorioEntityData
____exports.Converters = {}
local Converters = ____exports.Converters
do
local convertEntityStatus, EntityStatusRecord
function convertEntityStatus(status)
if status == nil then
return nil
else
return EntityStatusRecord[status]
end
end
function Converters.prototypeId(____type, name)
return (____type .. "/") .. name
end
local floor = math.floor
local mapEventIdToName = {}
for eventName, eventId in pairs(defines.events) do
mapEventIdToName[eventId] = eventName
@ -36,5 +51,86 @@ do
end
return masks
end
function Converters.convertResourceEntity(entity)
if entity.type ~= "resource" then
return nil
else
return {
type = FactorioEntityData.Type.Resource,
protoId = Converters.prototypeId(entity.type, entity.name),
status = convertEntityStatus(entity.status),
amount = entity.amount,
initialAmount = entity.initial_amount,
position = Converters.convertMapTablePosition(entity.position)
}
end
end
function Converters.prototypeIdEntity(entity)
return Converters.prototypeId(entity.type, entity.name)
end
function Converters.tilePositionToChunkPosition(mapPosition)
return {
floor(mapPosition.x / 32),
floor(mapPosition.y)
}
end
function Converters.mapPositionToChunkPosition(mapPosition)
return {
floor(mapPosition.x / 32),
floor(mapPosition.y)
}
end
function Converters.entityStatus(status)
if status == nil then
return nil
else
return EntityStatusRecord[status]
end
end
EntityStatusRecord = {
[defines.entity_status.cant_divide_segments] = EntityStatus.CANT_DIVIDE_SEGMENTS,
[defines.entity_status.charging] = EntityStatus.CHARGING,
[defines.entity_status.closed_by_circuit_network] = EntityStatus.CLOSED_BY_CIRCUIT_NETWORK,
[defines.entity_status.disabled] = EntityStatus.DISABLED,
[defines.entity_status.disabled_by_control_behavior] = EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR,
[defines.entity_status.disabled_by_script] = EntityStatus.DISABLED_BY_SCRIPT,
[defines.entity_status.discharging] = EntityStatus.DISCHARGING,
[defines.entity_status.fluid_ingredient_shortage] = EntityStatus.FLUID_INGREDIENT_SHORTAGE,
[defines.entity_status.fully_charged] = EntityStatus.FULLY_CHARGED,
[defines.entity_status.full_output] = EntityStatus.FULL_OUTPUT,
[defines.entity_status.item_ingredient_shortage] = EntityStatus.ITEM_INGREDIENT_SHORTAGE,
[defines.entity_status.launching_rocket] = EntityStatus.LAUNCHING_ROCKET,
[defines.entity_status.low_input_fluid] = EntityStatus.LOW_INPUT_FLUID,
[defines.entity_status.low_power] = EntityStatus.LOW_POWER,
[defines.entity_status.low_temperature] = EntityStatus.LOW_TEMPERATURE,
[defines.entity_status.marked_for_deconstruction] = EntityStatus.MARKED_FOR_DECONSTRUCTION,
[defines.entity_status.missing_required_fluid] = EntityStatus.MISSING_REQUIRED_FLUID,
[defines.entity_status.missing_science_packs] = EntityStatus.MISSING_SCIENCE_PACKS,
[defines.entity_status.networks_connected] = EntityStatus.NETWORKS_CONNECTED,
[defines.entity_status.networks_disconnected] = EntityStatus.NETWORKS_DISCONNECTED,
[defines.entity_status.normal] = EntityStatus.NORMAL,
[defines.entity_status.not_connected_to_rail] = EntityStatus.NOT_CONNECTED_TO_RAIL,
[defines.entity_status.not_plugged_in_electric_network] = EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK,
[defines.entity_status.no_ammo] = EntityStatus.NO_AMMO,
[defines.entity_status.no_fuel] = EntityStatus.NO_FUEL,
[defines.entity_status.no_ingredients] = EntityStatus.NO_INGREDIENTS,
[defines.entity_status.no_input_fluid] = EntityStatus.NO_INPUT_FLUID,
[defines.entity_status.no_minable_resources] = EntityStatus.NO_MINABLE_RESOURCES,
[defines.entity_status.no_modules_to_transmit] = EntityStatus.NO_MODULES_TO_TRANSMIT,
[defines.entity_status.no_power] = EntityStatus.NO_POWER,
[defines.entity_status.no_recipe] = EntityStatus.NO_RECIPE,
[defines.entity_status.no_research_in_progress] = EntityStatus.NO_RESEARCH_IN_PROGRESS,
[defines.entity_status.opened_by_circuit_network] = EntityStatus.OPENED_BY_CIRCUIT_NETWORK,
[defines.entity_status.out_of_logistic_network] = EntityStatus.OUT_OF_LOGISTIC_NETWORK,
[defines.entity_status.preparing_rocket_for_launch] = EntityStatus.PREPARING_ROCKET_FOR_LAUNCH,
[defines.entity_status.recharging_after_power_outage] = EntityStatus.RECHARGING_AFTER_POWER_OUTAGE,
[defines.entity_status.turned_off_during_daytime] = EntityStatus.TURNED_OFF_DURING_DAYTIME,
[defines.entity_status.waiting_for_source_items] = EntityStatus.WAITING_FOR_SOURCE_ITEMS,
[defines.entity_status.waiting_for_space_in_destination] = EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION,
[defines.entity_status.waiting_for_target_to_be_built] = EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT,
[defines.entity_status.waiting_for_train] = EntityStatus.WAITING_FOR_TRAIN,
[defines.entity_status.waiting_to_launch_rocket] = EntityStatus.WAITING_TO_LAUNCH_ROCKET,
[defines.entity_status.working] = EntityStatus.WORKING
}
end
return ____exports

View file

@ -1,105 +1,203 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local handleBuiltEntityEvent, handleMinedEntityEvent
local ____converters = require("main.events.converters")
local Converters = ____converters.Converters
local ____EventDataCache = require("main.cache.EventDataCache")
local EventDataCache = ____EventDataCache.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local FactorioEntityData = ____kafkatorio_2Dschema.FactorioEntityData
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
function handleBuiltEntityEvent(event)
local eventName = Converters.eventNameString(event.name)
if event.created_entity.unit_number == nil then
____exports.EntityUpdatesHandler = __TS__Class()
local EntityUpdatesHandler = ____exports.EntityUpdatesHandler
EntityUpdatesHandler.name = "EntityUpdatesHandler"
function EntityUpdatesHandler.prototype.____constructor(self)
end
function EntityUpdatesHandler.prototype.handleMinedTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
local key = {name = event.created_entity.name, protoType = event.created_entity.prototype.name, unitNumber = event.created_entity.unit_number}
EventDataCache:debounce(
key,
KafkatorioPacketData.Type.EntityUpdate,
local tilesByChunk = {}
for ____, tile in ipairs(event.tiles) do
local chunkPosition = Converters.tilePositionToChunkPosition(tile.position)
tilesByChunk[chunkPosition] = tile
end
for chunkPos, tiles in pairs(tilesByChunk) do
local entities = {}
for tile in pairs(tiles) do
local resources = ____exports.EntityUpdatesHandler:getTileResourceEntities(surface, chunkPos)
for ____, resource in ipairs(resources) do
entities[#entities + 1] = resource
end
end
____exports.EntityUpdatesHandler:throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick,
event.surface_index,
{chunkPos[1], chunkPos[2]},
entities
)
end
end
function EntityUpdatesHandler.prototype.handleMinedEntityEvent(self, event)
____exports.EntityUpdatesHandler:throttleEntityUpdate(event, event.entity)
end
function EntityUpdatesHandler.prototype.handleBuiltEntityEvent(self, event)
____exports.EntityUpdatesHandler:throttleEntityUpdate(event, event.created_entity)
end
function EntityUpdatesHandler.prototype.handleChunkGeneratedEvent(self, event)
local entities = ____exports.EntityUpdatesHandler:getAreaResourceEntities(event.surface, event.area)
____exports.EntityUpdatesHandler:throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick,
event.surface.index,
{event.position.x, event.position.y},
entities
)
end
function EntityUpdatesHandler.throttleEntityUpdate(self, event, entity)
if entity.unit_number == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local entityUpdateKey = {
protoId = Converters.prototypeIdEntity(entity),
surfaceIndex = entity.surface.index,
chunkPosition = Converters.mapPositionToChunkPosition(entity.position)
}
local ____FactorioEntityData_Type_Standard_2 = FactorioEntityData.Type.Standard
local ____Converters_prototypeIdEntity_result_3 = Converters.prototypeIdEntity(entity)
local ____entity_graphics_variation_4 = entity.graphics_variation
local ____entity_health_5 = entity.health
local ____entity_active_6 = entity.active
local ____entity_rotatable_7 = entity.rotatable
local ____entity_last_user_index_0 = entity.last_user
if ____entity_last_user_index_0 ~= nil then
____entity_last_user_index_0 = ____entity_last_user_index_0.index
end
local entityUpdate = {
type = ____FactorioEntityData_Type_Standard_2,
protoId = ____Converters_prototypeIdEntity_result_3,
graphicsVariation = ____entity_graphics_variation_4,
health = ____entity_health_5,
isActive = ____entity_active_6,
isRotatable = ____entity_rotatable_7,
lastUser = ____entity_last_user_index_0,
localisedDescription = nil,
localisedName = nil,
position = {entity.position.x, entity.position.y},
status = Converters.entityStatus(entity.status)
}
local entityKey = tostring(entity.unit_number)
EventDataCache:throttle(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
function(data)
if data.events == nil then
data.events = {}
end
local ____data_events_0, ____eventName_1 = data.events, eventName
if ____data_events_0[____eventName_1] == nil then
____data_events_0[____eventName_1] = {}
local ____data_events_8, ____eventName_9 = data.events, eventName
if ____data_events_8[____eventName_9] == nil then
____data_events_8[____eventName_9] = {}
end
local ____data_events_eventName_2 = data.events[eventName]
____data_events_eventName_2[#____data_events_eventName_2 + 1] = event.tick
data.chunkPosition = Converters.convertMapTablePosition(event.created_entity.position)
data.graphicsVariation = event.created_entity.graphics_variation
data.health = event.created_entity.health
data.isActive = event.created_entity.active
data.isRotatable = event.created_entity.rotatable
local ____data_5 = data
local ____event_created_entity_last_user_index_3 = event.created_entity.last_user
if ____event_created_entity_last_user_index_3 ~= nil then
____event_created_entity_last_user_index_3 = ____event_created_entity_last_user_index_3.index
local ____data_events_eventName_10 = data.events[eventName]
____data_events_eventName_10[#____data_events_eventName_10 + 1] = event.tick
if data.distinctEntities == nil then
data.distinctEntities = {}
end
____data_5.lastUser = ____event_created_entity_last_user_index_3
data.prototype = event.created_entity.prototype.name
data.distinctEntities[entityKey] = entityUpdate
end
)
end
function handleMinedEntityEvent(event)
local eventName = Converters.eventNameString(event.name)
if event.entity.unit_number == nil then
return
function EntityUpdatesHandler.throttleResourceEntitiesUpdate(self, eventName, eventTick, surfaceIndex, chunkPosition, entities)
local entitiesByProtoId = {}
for ____, entity in ipairs(entities) do
local ____entitiesByProtoId_11, ____entity_protoId_12 = entitiesByProtoId, entity.protoId
if ____entitiesByProtoId_11[____entity_protoId_12] == nil then
____entitiesByProtoId_11[____entity_protoId_12] = {}
end
local ____entitiesByProtoId_entity_protoId_13 = entitiesByProtoId[entity.protoId]
____entitiesByProtoId_entity_protoId_13[#____entitiesByProtoId_entity_protoId_13 + 1] = entity
end
local key = {name = event.entity.name, protoType = event.entity.prototype.name, unitNumber = event.entity.unit_number}
EventDataCache:debounce(
key,
KafkatorioPacketData.Type.EntityUpdate,
function(data)
if data.events == nil then
data.events = {}
for protoId, resourceEntities in pairs(entitiesByProtoId) do
local entityUpdateKey = {protoId = protoId, surfaceIndex = surfaceIndex, chunkPosition = {chunkPosition[1], chunkPosition[2]}}
EventDataCache:throttle(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
function(data)
if data.events == nil then
data.events = {}
end
local ____data_events_14, ____eventName_15 = data.events, eventName
if ____data_events_14[____eventName_15] == nil then
____data_events_14[____eventName_15] = {}
end
local ____data_events_eventName_16 = data.events[eventName]
____data_events_eventName_16[#____data_events_eventName_16 + 1] = eventTick
for ____, entity in ipairs(resourceEntities) do
local x, y = table.unpack(entity.position)
local entityKey = (tostring(x) .. ",") .. tostring(y)
if data.distinctEntities == nil then
data.distinctEntities = {}
end
data.distinctEntities[entityKey] = entity
end
end
local ____data_events_6, ____eventName_7 = data.events, eventName
if ____data_events_6[____eventName_7] == nil then
____data_events_6[____eventName_7] = {}
end
local ____data_events_eventName_8 = data.events[eventName]
____data_events_eventName_8[#____data_events_eventName_8 + 1] = event.tick
data.chunkPosition = Converters.convertMapTablePosition(event.entity.position)
data.graphicsVariation = event.entity.graphics_variation
data.health = event.entity.health
data.isActive = event.entity.active
data.isRotatable = event.entity.rotatable
local ____data_11 = data
local ____event_entity_last_user_index_9 = event.entity.last_user
if ____event_entity_last_user_index_9 ~= nil then
____event_entity_last_user_index_9 = ____event_entity_last_user_index_9.index
end
____data_11.lastUser = ____event_entity_last_user_index_9
data.prototype = event.entity.prototype.name
end
)
)
end
end
function EntityUpdatesHandler.getTileResourceEntities(self, surface, mapPosition)
local rawEntities = surface.find_entities_filtered({position = mapPosition, collision_mask = "resource-layer"})
local resources = {}
for ____, rawEntity in ipairs(rawEntities) do
local entity = Converters.convertResourceEntity(rawEntity)
if entity ~= nil then
resources[#resources + 1] = entity
end
end
return resources
end
function EntityUpdatesHandler.getAreaResourceEntities(self, surface, area)
local rawEntities = surface.find_entities_filtered({area = area, collision_mask = "resource-layer"})
local resources = {}
for ____, rawEntity in ipairs(rawEntities) do
local entity = Converters.convertResourceEntity(rawEntity)
if entity ~= nil then
resources[#resources + 1] = entity
end
end
return resources
end
local EntityUpdates = __TS__New(____exports.EntityUpdatesHandler)
____exports.default = EntityUpdates
script.on_event(
defines.events.on_built_entity,
function(e)
log("on_built_entity " .. tostring(e.tick))
handleBuiltEntityEvent(e)
EntityUpdates:handleBuiltEntityEvent(e)
end
)
script.on_event(
defines.events.on_robot_built_entity,
function(e)
log("on_robot_built_entity " .. tostring(e.tick))
handleBuiltEntityEvent(e)
EntityUpdates:handleBuiltEntityEvent(e)
end
)
script.on_event(
defines.events.on_player_mined_entity,
function(e)
log("on_player_mined_entity " .. tostring(e.tick))
handleMinedEntityEvent(e)
EntityUpdates:handleMinedEntityEvent(e)
end
)
script.on_event(
defines.events.on_robot_mined_entity,
function(e)
log("on_robot_mined_entity " .. tostring(e.tick))
handleMinedEntityEvent(e)
EntityUpdates:handleMinedEntityEvent(e)
end
)
return ____exports

View file

@ -1,9 +1,9 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local Map = ____lualib.Map
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local mapTilesUpdateDebounce, getTiles, getSurface, groupTiles, handleBuiltTileEvent, handleMinedTileEvent, Type
local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType
local ____converters = require("main.events.converters")
@ -12,65 +12,16 @@ local ____EventDataCache = require("main.cache.EventDataCache")
local EventUpdatesManager = ____EventDataCache.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
function mapTilesUpdateDebounce(surface, chunkPosition, tiles, event, updater, expirationDurationTicks)
if surface == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition}
EventUpdatesManager:debounce(
key,
Type.MapChunkUpdate,
function(data)
if data.tileDictionary == nil then
data.tileDictionary = {tilesXY = {}, protos = {}}
end
local protosCount = table_size(data.tileDictionary.protos)
for ____, tile in ipairs(tiles) do
local ____data_tileDictionary_protos_1, ____tile_name_2 = data.tileDictionary.protos, tile.name
if ____data_tileDictionary_protos_1[____tile_name_2] == nil then
local ____protosCount_0 = protosCount
protosCount = ____protosCount_0 + 1
____data_tileDictionary_protos_1[____tile_name_2] = ____protosCount_0
end
local protoKey = data.tileDictionary.protos[tile.name]
local xString = tostring(tile.position.x)
local yString = tostring(tile.position.y)
local ____data_tileDictionary_tilesXY_3, ____xString_4 = data.tileDictionary.tilesXY, xString
if ____data_tileDictionary_tilesXY_3[____xString_4] == nil then
____data_tileDictionary_tilesXY_3[____xString_4] = {}
end
data.tileDictionary.tilesXY[xString][yString] = protoKey
end
if data.events == nil then
data.events = {}
end
local ____data_events_5, ____eventName_6 = data.events, eventName
if ____data_events_5[____eventName_6] == nil then
____data_events_5[____eventName_6] = {}
end
local ____data_events_eventName_7 = data.events[eventName]
____data_events_eventName_7[#____data_events_eventName_7 + 1] = event.tick
if updater ~= nil then
updater(data)
end
end,
expirationDurationTicks
)
local ____entityUpdates = require("main.events.entityUpdates")
local EntityUpdates = ____entityUpdates.default
local Type = KafkatorioPacketData.Type
local MapChunkUpdateHandler = __TS__Class()
MapChunkUpdateHandler.name = "MapChunkUpdateHandler"
function MapChunkUpdateHandler.prototype.____constructor(self)
end
function getTiles(surface, area)
local ____surface_find_tiles_filtered_result_8 = surface
if ____surface_find_tiles_filtered_result_8 ~= nil then
____surface_find_tiles_filtered_result_8 = ____surface_find_tiles_filtered_result_8.find_tiles_filtered({area = area})
end
return ____surface_find_tiles_filtered_result_8 or ({})
end
function getSurface(surfaceIndex)
return game.surfaces[surfaceIndex]
end
function ____exports.handleChunkGeneratedEvent(event, expirationDurationTicks)
local tiles = getTiles(event.surface, event.area)
mapTilesUpdateDebounce(
function MapChunkUpdateHandler.prototype.handleChunkGeneratedEvent(self, event, expirationDurationTicks)
local tiles = event.surface.find_tiles_filtered({area = event.area})
MapChunkUpdateHandler:mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
@ -79,38 +30,19 @@ function ____exports.handleChunkGeneratedEvent(event, expirationDurationTicks)
expirationDurationTicks
)
end
function groupTiles(tiles)
local mapChunkPositionToTiles = __TS__New(Map)
for ____, tile in ipairs(tiles) do
local chunkPosition = {
math.floor(tile.position.x / 32),
math.floor(tile.position.y / 32)
}
if not mapChunkPositionToTiles:has(chunkPosition) then
mapChunkPositionToTiles:set(chunkPosition, {})
end
local ____mapChunkPositionToTiles_get_result_push_result_10 = mapChunkPositionToTiles:get(chunkPosition)
if ____mapChunkPositionToTiles_get_result_push_result_10 ~= nil then
local ____temp_11 = mapChunkPositionToTiles:get(chunkPosition)
____temp_11[#____temp_11 + 1] = tile
____mapChunkPositionToTiles_get_result_push_result_10 = nil
end
end
return mapChunkPositionToTiles
end
function handleBuiltTileEvent(event)
local surface = getSurface(event.surface_index)
function MapChunkUpdateHandler.prototype.handleBuiltTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
local eventName = Converters.eventNameString(event.name)
log("[handleBuiltTileEvent] undefined surface " .. eventName)
return
end
local tiles = Converters.convertPlacedTiles(event.tile, event.tiles)
local groupedTiles = groupTiles(tiles)
local groupedTiles = MapChunkUpdateHandler:groupTiles(tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(
MapChunkUpdateHandler:mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
@ -121,12 +53,15 @@ function handleBuiltTileEvent(event)
return
end
if isEventType(event, defines.events.on_robot_built_tile) then
local ____data_15 = data
local ____event_robot_unit_number_13 = event.robot
if ____event_robot_unit_number_13 ~= nil then
____event_robot_unit_number_13 = ____event_robot_unit_number_13.unit_number
local ____data_2 = data
local ____event_robot_unit_number_0 = event.robot
if ____event_robot_unit_number_0 ~= nil then
____event_robot_unit_number_0 = ____event_robot_unit_number_0.unit_number
end
____data_15.robot = {unitNumber = ____event_robot_unit_number_13 or nil, name = event.robot.name, protoType = event.robot.type}
____data_2.robot = {
unitNumber = ____event_robot_unit_number_0 or nil,
protoId = Converters.prototypeId(event.robot.type, event.robot.name)
}
return
end
local exhaustiveCheck = event
@ -134,19 +69,19 @@ function handleBuiltTileEvent(event)
)
end
end
function handleMinedTileEvent(event)
local surface = getSurface(event.surface_index)
function MapChunkUpdateHandler.prototype.handleMinedTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
local eventName = Converters.eventNameString(event.name)
log("[handleMinedTileEvent] undefined surface " .. eventName)
return
end
local tiles = Converters.convertRemovedTiles(surface, event.tiles)
local groupedTiles = groupTiles(tiles)
local groupedTiles = MapChunkUpdateHandler:groupTiles(tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(
MapChunkUpdateHandler:mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
@ -157,12 +92,15 @@ function handleMinedTileEvent(event)
return
end
if isEventType(event, defines.events.on_robot_mined_tile) then
local ____data_18 = data
local ____event_robot_unit_number_16 = event.robot
if ____event_robot_unit_number_16 ~= nil then
____event_robot_unit_number_16 = ____event_robot_unit_number_16.unit_number
local ____data_5 = data
local ____event_robot_unit_number_3 = event.robot
if ____event_robot_unit_number_3 ~= nil then
____event_robot_unit_number_3 = ____event_robot_unit_number_3.unit_number
end
____data_18.robot = {unitNumber = ____event_robot_unit_number_16 or nil, name = event.robot.name, protoType = event.robot.type}
____data_5.robot = {
unitNumber = ____event_robot_unit_number_3 or nil,
protoId = Converters.prototypeId(event.robot.type, event.robot.name)
}
return
end
local exhaustiveCheck = event
@ -170,78 +108,155 @@ function handleMinedTileEvent(event)
)
end
end
Type = KafkatorioPacketData.Type
function MapChunkUpdateHandler.prototype.handleScriptSetTilesEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
local groupedTiles = MapChunkUpdateHandler:groupTiles(event.tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
MapChunkUpdateHandler:mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
end
end
function MapChunkUpdateHandler.prototype.handlePreChunkDeleted(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
for ____, position in ipairs(event.positions) do
local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}}
EventUpdatesManager:debounce(
key,
Type.MapChunkTileUpdate,
function(data)
data.isDeleted = true
return true
end,
0
)
end
end
function MapChunkUpdateHandler.mapTilesUpdateDebounce(self, surface, chunkPosition, tiles, event, updater, expirationDurationTicks)
if surface == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition}
EventUpdatesManager:debounce(
key,
Type.MapChunkTileUpdate,
function(data)
if data.tileDictionary == nil then
data.tileDictionary = {tilesXY = {}, protos = {}}
end
local protosCount = table_size(data.tileDictionary.protos)
for ____, tile in ipairs(tiles) do
local protoId = Converters.prototypeId("tile", tile.name)
if data.tileDictionary.protos[protoId] == nil then
end
local ____data_tileDictionary_protos_7, ____protoId_8 = data.tileDictionary.protos, protoId
if ____data_tileDictionary_protos_7[____protoId_8] == nil then
local ____protosCount_6 = protosCount
protosCount = ____protosCount_6 + 1
____data_tileDictionary_protos_7[____protoId_8] = ____protosCount_6
end
local protoKey = data.tileDictionary.protos[protoId]
local xString = tostring(tile.position.x)
local yString = tostring(tile.position.y)
local ____data_tileDictionary_tilesXY_9, ____xString_10 = data.tileDictionary.tilesXY, xString
if ____data_tileDictionary_tilesXY_9[____xString_10] == nil then
____data_tileDictionary_tilesXY_9[____xString_10] = {}
end
data.tileDictionary.tilesXY[xString][yString] = protoKey
end
if data.events == nil then
data.events = {}
end
local ____data_events_11, ____eventName_12 = data.events, eventName
if ____data_events_11[____eventName_12] == nil then
____data_events_11[____eventName_12] = {}
end
local ____data_events_eventName_13 = data.events[eventName]
____data_events_eventName_13[#____data_events_eventName_13 + 1] = event.tick
if updater ~= nil then
updater(data)
end
end,
expirationDurationTicks
)
end
function MapChunkUpdateHandler.groupTiles(self, tiles)
local mapChunkPositionToTiles = __TS__New(Map)
for ____, tile in ipairs(tiles) do
local chunkPosition = {
math.floor(tile.position.x / 32),
math.floor(tile.position.y / 32)
}
if not mapChunkPositionToTiles:has(chunkPosition) then
mapChunkPositionToTiles:set(chunkPosition, {})
end
local ____mapChunkPositionToTiles_get_result_push_result_14 = mapChunkPositionToTiles:get(chunkPosition)
if ____mapChunkPositionToTiles_get_result_push_result_14 ~= nil then
local ____temp_15 = mapChunkPositionToTiles:get(chunkPosition)
____temp_15[#____temp_15 + 1] = tile
____mapChunkPositionToTiles_get_result_push_result_14 = nil
end
end
return mapChunkPositionToTiles
end
local MapChunkUpdate = __TS__New(MapChunkUpdateHandler)
____exports.default = MapChunkUpdate
script.on_event(
defines.events.on_chunk_generated,
function(e)
log("on_chunk_generated " .. tostring(e.tick))
____exports.handleChunkGeneratedEvent(e)
MapChunkUpdate:handleChunkGeneratedEvent(e)
EntityUpdates:handleChunkGeneratedEvent(e)
end
)
script.on_event(
defines.events.script_raised_set_tiles,
function(event)
log("script_raised_set_tiles " .. tostring(event.tick))
local surface = getSurface(event.surface_index)
if surface == nil then
return
end
local groupedTiles = groupTiles(event.tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
end
MapChunkUpdate:handleScriptSetTilesEvent(event)
end
)
script.on_event(
defines.events.on_player_built_tile,
function(e)
log("on_player_built_tile " .. tostring(e.tick))
handleBuiltTileEvent(e)
MapChunkUpdate:handleBuiltTileEvent(e)
end
)
script.on_event(
defines.events.on_robot_built_tile,
function(e)
log("on_robot_built_tile " .. tostring(e.tick))
handleBuiltTileEvent(e)
MapChunkUpdate:handleBuiltTileEvent(e)
end
)
script.on_event(
defines.events.on_player_mined_tile,
function(e)
log("on_player_mined_tile " .. tostring(e.tick))
handleMinedTileEvent(e)
MapChunkUpdate:handleMinedTileEvent(e)
EntityUpdates:handleMinedTileEvent(e)
end
)
script.on_event(
defines.events.on_robot_mined_tile,
function(e)
log("on_robot_mined_tile " .. tostring(e.tick))
handleMinedTileEvent(e)
MapChunkUpdate:handleMinedTileEvent(e)
end
)
script.on_event(
defines.events.on_pre_chunk_deleted,
function(e)
log("on_pre_chunk_deleted " .. tostring(e.tick))
local surface = getSurface(e.surface_index)
if surface == nil then
return
end
for ____, position in ipairs(e.positions) do
local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}}
EventUpdatesManager:debounce(
key,
Type.MapChunkUpdate,
function(data)
data.isDeleted = true
return true
end,
0
)
end
MapChunkUpdate:handlePreChunkDeleted(e)
end
)
return ____exports

View file

@ -115,7 +115,10 @@ script.on_event(
data.ticksToRespawn = player.ticks_to_respawn or nil
playerOnlineInfo(player, data)
if e.cause ~= nil then
data.diedCause = {unitNumber = e.cause.unit_number or nil, name = e.cause.name, protoType = e.cause.type}
data.diedCause = {
unitNumber = e.cause.unit_number or nil,
protoId = Converters.prototypeId(e.cause.type, e.cause.name)
}
end
end
)

View file

@ -22,7 +22,7 @@ function getMapTilePrototypes()
for ____, tile in pairs(game.tile_prototypes) do
tiles[#tiles + 1] = {
type = FactorioPrototype.Type.MapTile,
name = tile.name,
protoId = Converters.prototypeId("tile", tile.name),
order = tile.order,
layer = tile.layer,
collisionMasks = Converters.convertCollisionMaskToNames(tile.collision_mask),

View file

@ -5,12 +5,12 @@ local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType
local ____converters = require("main.events.converters")
local Converters = ____converters.Converters
local ____mapChunkUpdates = require("main.events.mapChunkUpdates")
local handleChunkGeneratedEvent = ____mapChunkUpdates.handleChunkGeneratedEvent
local ____surfaceUpdates = require("main.events.surfaceUpdates")
local handleSurfaceUpdate = ____surfaceUpdates.handleSurfaceUpdate
local ____EventDataCache = require("main.cache.EventDataCache")
local EventCacheService = ____EventDataCache.default
local ____mapChunkUpdates = require("main.events.mapChunkUpdates")
local MapChunkUpdate = ____mapChunkUpdates.default
script.on_event(
defines.events.on_tick,
function(event)
@ -38,7 +38,7 @@ script.on_event(
if isEventType(event, defines.events.on_chunk_generated) then
local eName = Converters.eventNameString(event.name)
log((((("[on_tick:" .. tostring(event.tick)) .. "] dequed event ") .. eName) .. ", delay ") .. tostring(i))
handleChunkGeneratedEvent(event, i)
MapChunkUpdate:handleChunkGeneratedEvent(event, i)
i = i + 1
end
end

View file

@ -34,7 +34,7 @@ end
function KafkatorioSettingsConfig.cacheDurationSettingName(self, ____type)
return "kafkatorio:cache-expiration-seconds_" .. __TS__StringReplaceAll(____type, ".", "-")
end
KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds = {["kafkatorio.packet.keyed.PlayerUpdate"] = 0.5, ["kafkatorio.packet.keyed.MapChunkUpdate"] = 30, ["kafkatorio.packet.keyed.EntityUpdate"] = 5}
KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds = {["kafkatorio.packet.keyed.PlayerUpdate"] = 0.5, ["kafkatorio.packet.keyed.MapChunkTileUpdate"] = 30, ["kafkatorio.packet.keyed.MapChunkEntityUpdate"] = 30, ["kafkatorio.packet.keyed.EntityUpdate"] = 5}
local KafkatorioSettings = __TS__New(____exports.KafkatorioSettingsConfig)
____exports.default = KafkatorioSettings
return ____exports

View file

@ -1,14 +1,16 @@
[mod-setting-name]
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-PlayerUpdate=Player update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkUpdate=Map update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkTileUpdate=Map tile update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkEntityUpdate=Entity update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-EntityUpdate=Entity update rate (seconds)
[mod-setting-description]
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-PlayerUpdate=How frequently Player updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (0.25 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkUpdate=How frequently Map updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkTileUpdate=How frequently Map updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkEntityUpdate=How frequently entity updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-EntityUpdate=How frequently Entity updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (5 seconds) is probably fine!

View file

@ -1,7 +1,7 @@
import {initGlobal} from "./main/global-init";
import {emitPrototypes} from "./main/events/prototypeUpdates";
require("./main/events/registerHandlers")
require("./main/events/registerEventListeners")
require("./main/events/mapChunkUpdates")
require("./main/events/playerUpdates")
require("./main/events/entityUpdates")

View file

@ -14,7 +14,8 @@ export type KafkatorioPacketData =
| KafkatorioPacketData.ConsoleCommandUpdate
| KafkatorioPacketData.EntityUpdate
| KafkatorioPacketData.Error
| KafkatorioPacketData.MapChunkUpdate
| KafkatorioPacketData.MapChunkEntityUpdate
| KafkatorioPacketData.MapChunkTileUpdate
| KafkatorioPacketData.PlayerUpdate
| KafkatorioPacketData.PrototypesUpdate
| KafkatorioPacketData.SurfaceUpdate;
@ -27,7 +28,8 @@ export namespace KafkatorioPacketData {
PrototypesUpdate = "kafkatorio.packet.instant.PrototypesUpdate",
SurfaceUpdate = "kafkatorio.packet.instant.SurfaceUpdate",
EntityUpdate = "kafkatorio.packet.keyed.EntityUpdate",
MapChunkUpdate = "kafkatorio.packet.keyed.MapChunkUpdate",
MapChunkEntityUpdate = "kafkatorio.packet.keyed.MapChunkEntityUpdate",
MapChunkTileUpdate = "kafkatorio.packet.keyed.MapChunkTileUpdate",
PlayerUpdate = "kafkatorio.packet.keyed.PlayerUpdate",
Error = "kafkatorio.packet.KafkatorioPacketData.Error",
}
@ -77,12 +79,18 @@ export namespace KafkatorioPacketData {
lastUser?: UInt | null;
localisedDescription?: string | null;
localisedName?: string | null;
prototype?: PrototypeName | null;
}
export interface MapChunkUpdate {
type: KafkatorioPacketData.Type.MapChunkUpdate;
key: MapChunkUpdateKey;
export interface MapChunkEntityUpdate {
type: KafkatorioPacketData.Type.MapChunkEntityUpdate;
key: MapChunkEntityUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
distinctEntities?: { [key: string]: FactorioEntityData } | null;
}
export interface MapChunkTileUpdate {
type: KafkatorioPacketData.Type.MapChunkTileUpdate;
key: MapChunkTileUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
player?: PlayerIndex | null;
robot?: EntityIdentifiersData | null;
@ -132,16 +140,11 @@ export interface ConfigurationUpdateGameData {
newVersion?: string | null;
}
export type PlayerIndex = UInt;
export type SurfaceIndex = UInt;
export type Double = double;
export interface EntityUpdateKey {
unitNumber: UnitNumber;
name: string;
protoType: string;
protoId: PrototypeId;
}
export type MapEntityPosition = [
@ -153,32 +156,33 @@ export type UByte = uint8;
export type Float = float;
export type PrototypeName = string;
export interface MapChunkEntityUpdateKey {
protoId: PrototypeId;
surfaceIndex: SurfaceIndex;
chunkPosition: MapChunkPosition;
}
export interface MapChunkUpdateKey {
export interface MapChunkTileUpdateKey {
chunkPosition: MapChunkPosition;
surfaceIndex: SurfaceIndex;
}
export interface EntityIdentifiersData {
unitNumber?: UnitNumber | null;
name: string;
protoType: string;
protoId: PrototypeId;
}
export type ForceIndex = UInt;
export type ForceIndex = UInt & { __ForceIndex__: void };
export interface MapTileDictionary {
tilesXY: { [key: string]: { [key: string]: PrototypeKey } };
protos: { [key: PrototypeName]: PrototypeKey };
protos: { [key: PrototypeId]: PrototypeKey };
}
export interface PlayerUpdateKey {
index: PlayerIndex;
}
export type UnitNumber = UInt;
export type Colour = [
red: Float,
green: Float,
@ -206,7 +210,7 @@ export namespace FactorioPrototype {
export interface Entity {
type: FactorioPrototype.Type.Entity;
name: PrototypeName;
protoId: PrototypeId;
mapColour: Colour;
protoType: string;
objectName: string;
@ -220,7 +224,7 @@ export namespace FactorioPrototype {
export interface MapTile {
type: FactorioPrototype.Type.MapTile;
name: PrototypeName;
protoId: PrototypeId;
mapColour: Colour;
layer: UInt;
collisionMasks: List;
@ -229,7 +233,9 @@ export namespace FactorioPrototype {
}
}
export type EventName = string;
export type PrototypeId = string & { __PrototypeId__: void };
export type EventName = string & { __EventName__: void };
export type Byte = int8;
@ -238,22 +244,114 @@ export type MapChunkPosition = [
y: Int,
];
export type FactorioEntityData =
| FactorioEntityData.Resource
| FactorioEntityData.Standard;
export namespace FactorioEntityData {
export enum Type {
Resource = "kafkatorio.entity.FactorioEntityData.Resource",
Standard = "kafkatorio.entity.FactorioEntityData.Standard",
}
export interface Resource {
type: FactorioEntityData.Type.Resource;
protoId: PrototypeId;
status?: EntityStatus | null;
position: MapEntityPosition;
amount: UInt;
initialAmount?: UInt | null;
}
export interface Standard {
type: FactorioEntityData.Type.Standard;
protoId: PrototypeId;
status?: EntityStatus | null;
position: MapEntityPosition;
graphicsVariation?: UByte | null;
health?: Float | null;
isActive?: boolean | null;
isRotatable?: boolean | null;
lastUser?: UInt | null;
localisedDescription?: string | null;
localisedName?: string | null;
}
}
export interface MiningProperties {
canBeMined: boolean;
products: MinedProduct[] | null;
}
export type List = any;
export type List = any & { __List__: void };
export type PrototypeKey = Int;
export interface MinedProduct {
type: MinedProductType;
name: PrototypeName;
amount: Double | null;
export enum EntityStatus {
CANT_DIVIDE_SEGMENTS = "CANT_DIVIDE_SEGMENTS",
CHARGING = "CHARGING",
CLOSED_BY_CIRCUIT_NETWORK = "CLOSED_BY_CIRCUIT_NETWORK",
DISABLED = "DISABLED",
DISABLED_BY_CONTROL_BEHAVIOR = "DISABLED_BY_CONTROL_BEHAVIOR",
DISABLED_BY_SCRIPT = "DISABLED_BY_SCRIPT",
DISCHARGING = "DISCHARGING",
FLUID_INGREDIENT_SHORTAGE = "FLUID_INGREDIENT_SHORTAGE",
FULLY_CHARGED = "FULLY_CHARGED",
FULL_OUTPUT = "FULL_OUTPUT",
ITEM_INGREDIENT_SHORTAGE = "ITEM_INGREDIENT_SHORTAGE",
LAUNCHING_ROCKET = "LAUNCHING_ROCKET",
LOW_INPUT_FLUID = "LOW_INPUT_FLUID",
LOW_POWER = "LOW_POWER",
LOW_TEMPERATURE = "LOW_TEMPERATURE",
MARKED_FOR_DECONSTRUCTION = "MARKED_FOR_DECONSTRUCTION",
MISSING_REQUIRED_FLUID = "MISSING_REQUIRED_FLUID",
MISSING_SCIENCE_PACKS = "MISSING_SCIENCE_PACKS",
NETWORKS_CONNECTED = "NETWORKS_CONNECTED",
NETWORKS_DISCONNECTED = "NETWORKS_DISCONNECTED",
NORMAL = "NORMAL",
NOT_CONNECTED_TO_RAIL = "NOT_CONNECTED_TO_RAIL",
NOT_PLUGGED_IN_ELECTRIC_NETWORK = "NOT_PLUGGED_IN_ELECTRIC_NETWORK",
NO_AMMO = "NO_AMMO",
NO_FUEL = "NO_FUEL",
NO_INGREDIENTS = "NO_INGREDIENTS",
NO_INPUT_FLUID = "NO_INPUT_FLUID",
NO_MINABLE_RESOURCES = "NO_MINABLE_RESOURCES",
NO_MODULES_TO_TRANSMIT = "NO_MODULES_TO_TRANSMIT",
NO_POWER = "NO_POWER",
NO_RECIPE = "NO_RECIPE",
NO_RESEARCH_IN_PROGRESS = "NO_RESEARCH_IN_PROGRESS",
OPENED_BY_CIRCUIT_NETWORK = "OPENED_BY_CIRCUIT_NETWORK",
OUT_OF_LOGISTIC_NETWORK = "OUT_OF_LOGISTIC_NETWORK",
PREPARING_ROCKET_FOR_LAUNCH = "PREPARING_ROCKET_FOR_LAUNCH",
RECHARGING_AFTER_POWER_OUTAGE = "RECHARGING_AFTER_POWER_OUTAGE",
TURNED_OFF_DURING_DAYTIME = "TURNED_OFF_DURING_DAYTIME",
WAITING_FOR_SOURCE_ITEMS = "WAITING_FOR_SOURCE_ITEMS",
WAITING_FOR_SPACE_IN_DESTINATION = "WAITING_FOR_SPACE_IN_DESTINATION",
WAITING_FOR_TARGET_TO_BE_BUILT = "WAITING_FOR_TARGET_TO_BE_BUILT",
WAITING_FOR_TRAIN = "WAITING_FOR_TRAIN",
WAITING_TO_LAUNCH_ROCKET = "WAITING_TO_LAUNCH_ROCKET",
WORKING = "WORKING",
}
export enum MinedProductType {
item = "item",
fluid = "fluid",
export type PrototypeKey = Int & { __PrototypeKey__: void };
export type MinedProduct =
| MinedProduct.MinedProductFluid
| MinedProduct.MinedProductItem;
export namespace MinedProduct {
export enum Type {
MinedProductFluid = "kafkatorio.resource.MinedProduct.MinedProductFluid",
MinedProductItem = "kafkatorio.resource.MinedProduct.MinedProductItem",
}
export interface MinedProductFluid {
type: MinedProduct.Type.MinedProductFluid;
amount?: Double | null;
resultProtoId: PrototypeId;
}
export interface MinedProductItem {
type: MinedProduct.Type.MinedProductItem;
amount?: Double | null;
resultProtoId: PrototypeId;
}
}

View file

@ -101,7 +101,7 @@ export namespace EventUpdates {
let countExpired = 0
let countTotal = 0
const expiredData: Array<KafkatorioKeyedPacketData> = []
for (let [key, entry] of global.cache) {
for (const [key, entry] of global.cache) {
countTotal++
if (this.isExpired(entry)) {
countExpired++

View file

@ -23,7 +23,7 @@ script.on_event(
const update: KafkatorioPacketData.ConsoleCommandUpdate = {
type: Type.ConsoleCommandUpdate,
authorPlayerIndex: event.player_index ?? null,
authorPlayerIndex: event.player_index ?? null,
command: event.command,
parameters: event.parameters,
}

View file

@ -1,16 +1,25 @@
import {EventName} from "../types";
import {Colour, MapChunkPosition} from "../../generated/kafkatorio-schema";
import {DefinedEventName} from "../types";
import {
Colour,
EntityStatus,
EventName,
FactorioEntityData,
MapChunkPosition,
PrototypeId,
} from "../../generated/kafkatorio-schema";
export namespace Converters {
const mapEventIdToName = new LuaTable<defines.Events, EventName>()
import floor = math.floor;
const mapEventIdToName = new LuaTable<defines.Events, DefinedEventName>()
for (const [eventName, eventId] of pairs(defines.events)) {
mapEventIdToName.set(eventId, eventName)
}
export function eventNameString(event: defines.Events): EventName {
return mapEventIdToName.get(event)
return mapEventIdToName.get(event) as EventName
}
@ -60,10 +69,115 @@ export namespace Converters {
export function convertCollisionMaskToNames(cm: CollisionMask): string[] {
let masks: string[] = []
for (let [name] of pairs(cm)) {
for (const [name] of pairs(cm)) {
masks.push(name)
}
return masks
}
export function convertResourceEntity(
entity: LuaEntity
): FactorioEntityData.Resource | null {
if (entity.type != "resource") {
return null
} else {
return {
type: FactorioEntityData.Type.Resource,
protoId: prototypeId(entity.type, entity.name),
status: convertEntityStatus(entity.status),
amount: entity.amount,
initialAmount: entity.initial_amount,
position: convertMapTablePosition(entity.position),
}
}
}
function convertEntityStatus(
status: defines.entity_status | undefined
): EntityStatus | null {
if (status == undefined) {
return null
} else {
return EntityStatusRecord[status]
}
}
export function prototypeIdEntity(entity: LuaEntity): PrototypeId {
return prototypeId(entity.type, entity.name)
}
export function prototypeId(type: string, name: string): PrototypeId {
return `${type}/${name}` as PrototypeId
}
export function tilePositionToChunkPosition(mapPosition: TilePositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
}
export function mapPositionToChunkPosition(mapPosition: MapPositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
}
export function entityStatus(status: defines.entity_status | undefined): EntityStatus | undefined {
if (status == undefined) {
return undefined
} else {
return EntityStatusRecord[status]
}
}
const EntityStatusRecord: Record<defines.entity_status, EntityStatus> = {
//@formatter:off
[ defines.entity_status.cant_divide_segments ]: EntityStatus.CANT_DIVIDE_SEGMENTS,
[ defines.entity_status.charging ]: EntityStatus.CHARGING,
[ defines.entity_status.closed_by_circuit_network ]: EntityStatus.CLOSED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.disabled ]: EntityStatus.DISABLED,
[ defines.entity_status.disabled_by_control_behavior ]: EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR,
[ defines.entity_status.disabled_by_script ]: EntityStatus.DISABLED_BY_SCRIPT,
[ defines.entity_status.discharging ]: EntityStatus.DISCHARGING,
[ defines.entity_status.fluid_ingredient_shortage ]: EntityStatus.FLUID_INGREDIENT_SHORTAGE,
[ defines.entity_status.fully_charged ]: EntityStatus.FULLY_CHARGED,
[ defines.entity_status.full_output ]: EntityStatus.FULL_OUTPUT,
[ defines.entity_status.item_ingredient_shortage ]: EntityStatus.ITEM_INGREDIENT_SHORTAGE,
[ defines.entity_status.launching_rocket ]: EntityStatus.LAUNCHING_ROCKET,
[ defines.entity_status.low_input_fluid ]: EntityStatus.LOW_INPUT_FLUID,
[ defines.entity_status.low_power ]: EntityStatus.LOW_POWER,
[ defines.entity_status.low_temperature ]: EntityStatus.LOW_TEMPERATURE,
[ defines.entity_status.marked_for_deconstruction ]: EntityStatus.MARKED_FOR_DECONSTRUCTION,
[ defines.entity_status.missing_required_fluid ]: EntityStatus.MISSING_REQUIRED_FLUID,
[ defines.entity_status.missing_science_packs ]: EntityStatus.MISSING_SCIENCE_PACKS,
[ defines.entity_status.networks_connected ]: EntityStatus.NETWORKS_CONNECTED,
[ defines.entity_status.networks_disconnected ]: EntityStatus.NETWORKS_DISCONNECTED,
[ defines.entity_status.normal ]: EntityStatus.NORMAL,
[ defines.entity_status.not_connected_to_rail ]: EntityStatus.NOT_CONNECTED_TO_RAIL,
[ defines.entity_status.not_plugged_in_electric_network ]: EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK,
[ defines.entity_status.no_ammo ]: EntityStatus.NO_AMMO,
[ defines.entity_status.no_fuel ]: EntityStatus.NO_FUEL,
[ defines.entity_status.no_ingredients ]: EntityStatus.NO_INGREDIENTS,
[ defines.entity_status.no_input_fluid ]: EntityStatus.NO_INPUT_FLUID,
[ defines.entity_status.no_minable_resources ]: EntityStatus.NO_MINABLE_RESOURCES,
[ defines.entity_status.no_modules_to_transmit ]: EntityStatus.NO_MODULES_TO_TRANSMIT,
[ defines.entity_status.no_power ]: EntityStatus.NO_POWER,
[ defines.entity_status.no_recipe ]: EntityStatus.NO_RECIPE,
[ defines.entity_status.no_research_in_progress ]: EntityStatus.NO_RESEARCH_IN_PROGRESS,
[ defines.entity_status.opened_by_circuit_network ]: EntityStatus.OPENED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.out_of_logistic_network ]: EntityStatus.OUT_OF_LOGISTIC_NETWORK,
[ defines.entity_status.preparing_rocket_for_launch ]: EntityStatus.PREPARING_ROCKET_FOR_LAUNCH,
[ defines.entity_status.recharging_after_power_outage ]: EntityStatus.RECHARGING_AFTER_POWER_OUTAGE,
[ defines.entity_status.turned_off_during_daytime ]: EntityStatus.TURNED_OFF_DURING_DAYTIME,
[ defines.entity_status.waiting_for_source_items ]: EntityStatus.WAITING_FOR_SOURCE_ITEMS,
[ defines.entity_status.waiting_for_space_in_destination ]: EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION,
[ defines.entity_status.waiting_for_target_to_be_built ]: EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT,
[ defines.entity_status.waiting_for_train ]: EntityStatus.WAITING_FOR_TRAIN,
[ defines.entity_status.waiting_to_launch_rocket ]: EntityStatus.WAITING_TO_LAUNCH_ROCKET,
[ defines.entity_status.working ]: EntityStatus.WORKING,
//@formatter:on
}
}

View file

@ -1,97 +1,276 @@
import {Converters} from "./converters";
import EventDataCache from "../cache/EventDataCache";
import {
EntityUpdateKey,
KafkatorioPacketData
EventName,
FactorioEntityData,
KafkatorioPacketData,
MapChunkPosition,
PrototypeId,
Tick,
} from "../../generated/kafkatorio-schema";
export class EntityUpdatesHandler {
handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == null) {
return
}
const tilesByChunk: LuaTable<MapChunkPosition, OldTileAndPosition> = new LuaTable<MapChunkPosition, OldTileAndPosition>()
for (const tile of event.tiles) {
const chunkPosition = Converters.tilePositionToChunkPosition(tile.position)
tilesByChunk.set(chunkPosition, tile)
}
for (const [chunkPos, tiles] of pairs(tilesByChunk)) {
const entities: FactorioEntityData.Resource[] = []
for (const tile in tiles) {
const resources: FactorioEntityData.Resource[] = EntityUpdatesHandler.getTileResourceEntities(surface, chunkPos)
for (const resource of resources) {
entities[entities.length] = resource
}
}
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface_index,
[chunkPos[0], chunkPos[1]],
entities,
)
}
}
handleMinedEntityEvent(
event: OnPlayerMinedEntityEvent | OnRobotMinedEntityEvent,
) {
EntityUpdatesHandler.throttleEntityUpdate(event, event.entity)
}
handleBuiltEntityEvent(
event: OnBuiltEntityEvent | OnRobotBuiltEntityEvent,
) {
EntityUpdatesHandler.throttleEntityUpdate(event, event.created_entity)
}
handleChunkGeneratedEvent(event: OnChunkGeneratedEvent) {
const entities = EntityUpdatesHandler.getAreaResourceEntities(event.surface, event.area)
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface.index,
[event.position.x, event.position.y],
entities,
)
}
private static throttleEntityUpdate(event: EntityUpdateEvent, entity: LuaEntity) {
if (entity.unit_number == undefined) {
// ignore for now - I'm not sure how to track entities without a unit_number
return
}
const eventName = Converters.eventNameString(event.name)
const entityUpdateKey: KafkatorioPacketData.MapChunkEntityUpdate["key"] = {
protoId: Converters.prototypeIdEntity(entity),
surfaceIndex: entity.surface.index,
chunkPosition: Converters.mapPositionToChunkPosition(entity.position)
}
const entityUpdate: FactorioEntityData.Standard = {
type: FactorioEntityData.Type.Standard,
protoId: Converters.prototypeIdEntity(entity),
graphicsVariation: entity.graphics_variation,
health: entity.health,
isActive: entity.active,
isRotatable: entity.rotatable,
lastUser: entity.last_user?.index,
localisedDescription: null,
localisedName: null,
position: [entity.position.x, entity.position.y],
status: Converters.entityStatus(entity.status),
}
const entityKey: string = `${entity.unit_number}`
EventDataCache.throttle<KafkatorioPacketData.MapChunkEntityUpdate>(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entityUpdate
}
)
}
private static throttleResourceEntitiesUpdate(
eventName: EventName,
eventTick: Tick,
surfaceIndex: SurfaceIndex,
chunkPosition: MapChunkPosition,
entities: FactorioEntityData.Resource[],
) {
// first group by prototype ID
const entitiesByProtoId: { [protoId: PrototypeId]: FactorioEntityData.Resource[] } = {}
for (const entity of entities) {
entitiesByProtoId[entity.protoId] ??= []
entitiesByProtoId[entity.protoId].push(entity)
}
for (const [protoId, resourceEntities] of pairs(entitiesByProtoId)) {
// create an update for each prototype
const entityUpdateKey: KafkatorioPacketData.MapChunkEntityUpdate["key"] = {
protoId: protoId,
surfaceIndex: surfaceIndex,
chunkPosition: [chunkPosition[0], chunkPosition[1]],
}
EventDataCache.throttle<KafkatorioPacketData.MapChunkEntityUpdate>(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(eventTick)
for (const entity of resourceEntities) {
const [x, y] = entity.position
const entityKey: string = `${x},${y}`
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entity
}
}
)
}
}
/** Get all resources on a tile-position */
private static getTileResourceEntities(
surface: LuaSurface,
mapPosition: MapPosition,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
position: mapPosition,
collision_mask: "resource-layer",
}
)
const resources: FactorioEntityData.Resource[] = []
for (const rawEntity of rawEntities) {
const entity = Converters.convertResourceEntity(rawEntity)
if (entity != null) {
resources[resources.length] = entity
}
}
return resources
}
/** Get all resources within a map-area */
private static getAreaResourceEntities(
surface: LuaSurface,
area: BoundingBoxTable,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
area: area,
collision_mask: "resource-layer",
}
)
const resources: FactorioEntityData.Resource[] = []
for (const rawEntity of rawEntities) {
const entity = Converters.convertResourceEntity(rawEntity)
if (entity != null) {
resources[resources.length] = entity
}
}
return resources
}
}
const EntityUpdates = new EntityUpdatesHandler()
export default EntityUpdates
script.on_event(defines.events.on_built_entity, (e: OnBuiltEntityEvent) => {
log(`on_built_entity ${e.tick}`)
handleBuiltEntityEvent(e)
EntityUpdates.handleBuiltEntityEvent(e)
})
script.on_event(defines.events.on_robot_built_entity, (e: OnRobotBuiltEntityEvent) => {
log(`on_robot_built_entity ${e.tick}`)
handleBuiltEntityEvent(e)
EntityUpdates.handleBuiltEntityEvent(e)
})
function handleBuiltEntityEvent(event: OnBuiltEntityEvent | OnRobotBuiltEntityEvent) {
const eventName = Converters.eventNameString(event.name)
if (event.created_entity.unit_number == undefined) {
return
}
const key: EntityUpdateKey = {
name: event.created_entity.name,
protoType: event.created_entity.prototype.name,
unitNumber: event.created_entity.unit_number
}
EventDataCache.debounce<KafkatorioPacketData.EntityUpdate>(
key,
KafkatorioPacketData.Type.EntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
data.chunkPosition = Converters.convertMapTablePosition(event.created_entity.position)
data.graphicsVariation = event.created_entity.graphics_variation
data.health = event.created_entity.health
data.isActive = event.created_entity.active
data.isRotatable = event.created_entity.rotatable
data.lastUser = event.created_entity.last_user?.index
data.prototype = event.created_entity.prototype.name
}
)
}
script.on_event(defines.events.on_player_mined_entity, (e: OnPlayerMinedEntityEvent) => {
log(`on_player_mined_entity ${e.tick}`)
handleMinedEntityEvent(e)
EntityUpdates.handleMinedEntityEvent(e)
})
script.on_event(defines.events.on_robot_mined_entity, (e: OnRobotMinedEntityEvent) => {
log(`on_robot_mined_entity ${e.tick}`)
handleMinedEntityEvent(e)
EntityUpdates.handleMinedEntityEvent(e)
})
function handleMinedEntityEvent(event: OnPlayerMinedEntityEvent | OnRobotMinedEntityEvent) {
const eventName = Converters.eventNameString(event.name)
type EntityUpdateEvent =
| OnBuiltEntityEvent
if (event.entity.unit_number == undefined) {
return
}
| OnEntityClonedEvent
| OnEntityDamagedEvent
| OnEntityDestroyedEvent
| OnEntityDiedEvent
| OnEntityLogisticSlotChangedEvent
| OnEntityRenamedEvent
| OnEntitySettingsPastedEvent
| OnEntitySpawnedEvent
const key: EntityUpdateKey = {
name: event.entity.name,
protoType: event.entity.prototype.name,
unitNumber: event.entity.unit_number
}
| OnPlayerMinedEntityEvent
| OnPlayerRepairedEntityEvent
| OnPlayerRotatedEntityEvent
EventDataCache.debounce<KafkatorioPacketData.EntityUpdate>(
key,
KafkatorioPacketData.Type.EntityUpdate,
data => {
| OnRobotMinedEntityEvent
| OnRobotBuiltEntityEvent
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
| OnPostEntityDiedEvent
| OnPreEntitySettingsPastedEvent
data.chunkPosition = Converters.convertMapTablePosition(event.entity.position)
data.graphicsVariation = event.entity.graphics_variation
data.health = event.entity.health
data.isActive = event.entity.active
data.isRotatable = event.entity.rotatable
data.lastUser = event.entity.last_user?.index
data.prototype = event.entity.prototype.name
}
)
}
| OnSelectedEntityChangedEvent
| OnTriggerCreatedEntityEvent

View file

@ -3,129 +3,270 @@ import {Converters} from "./converters";
import EventUpdatesManager, {EventUpdates} from "../cache/EventDataCache";
import {
KafkatorioPacketData,
MapChunkPosition
MapChunkPosition, PrototypeId,
PrototypeKey,
} from "../../generated/kafkatorio-schema";
import EntityUpdates from "./entityUpdates";
import CacheKey = EventUpdates.PacketKey;
import Type = KafkatorioPacketData.Type;
type MapChunkUpdater = (data: KafkatorioPacketData.MapChunkUpdate) => void
type MapChunkTileUpdater = (data: KafkatorioPacketData.MapChunkTileUpdate) => void
type MapTileUpdateEvent =
| OnChunkGeneratedEvent
export type MapTileChangeEvent =
| OnPlayerBuiltTileEvent
| OnPlayerMinedEntityEvent
| OnPlayerMinedTileEvent
| OnRobotBuiltTileEvent
| OnRobotMinedEntityEvent
| OnRobotMinedTileEvent
| ScriptRaisedSetTilesEvent
function mapTilesUpdateDebounce(
surface: LuaSurface | undefined,
chunkPosition: MapChunkPosition,
tiles: TileRead[],
event: MapTileUpdateEvent,
updater?: MapChunkUpdater,
expirationDurationTicks?: uint,
) {
export type MapChunkUpdateEvent =
| OnChunkChartedEvent
| OnChunkDeletedEvent
| OnChunkGeneratedEvent
| OnPreChunkDeletedEvent
if (surface == undefined) {
return
class MapChunkUpdateHandler {
handleChunkGeneratedEvent(
event: OnChunkGeneratedEvent,
expirationDurationTicks?: uint,
) {
const tiles = event.surface.find_tiles_filtered({area: event.area})
MapChunkUpdateHandler.mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
event,
undefined,
expirationDurationTicks,
)
}
const eventName = Converters.eventNameString(event.name)
const key: KafkatorioPacketData.MapChunkUpdate["key"] = {
surfaceIndex: surface.index,
chunkPosition: chunkPosition,
handleBuiltTileEvent(event: OnPlayerBuiltTileEvent | OnRobotBuiltTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleBuiltTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertPlacedTiles(event.tile, event.tiles)
const groupedTiles = MapChunkUpdateHandler.groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
MapChunkUpdateHandler.mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_built_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_built_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
protoId: Converters.prototypeId(event.robot.type, event.robot.name),
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkUpdate>(
key,
Type.MapChunkUpdate,
data => {
// tile dictionary update
if (data.tileDictionary == undefined) {
data.tileDictionary = {
tilesXY: {},
protos: {},
}
}
handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleMinedTileEvent] undefined surface ${eventName}`)
return
}
let protosCount: uint = table_size(data.tileDictionary.protos)
for (const tile of tiles) {
data.tileDictionary.protos[tile.name] ??= protosCount++
const tiles: TileRead[] = Converters.convertRemovedTiles(surface, event.tiles)
const groupedTiles = MapChunkUpdateHandler.groupTiles(tiles)
const protoKey = data.tileDictionary.protos[tile.name]
for (const [chunkPos, tiles] of groupedTiles) {
const xString = `${tile.position.x}`
const yString = `${tile.position.y}`
data.tileDictionary.tilesXY[xString] ??= {}
data.tileDictionary.tilesXY[xString][yString] = protoKey
}
// events count update
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
// apply mutator
if (updater != undefined) {
updater(data)
}
},
expirationDurationTicks,
)
}
MapChunkUpdateHandler.mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_mined_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_mined_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
protoId: Converters.prototypeId(event.robot.type, event.robot.name),
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
function getTiles(
surface: LuaSurface | undefined,
area: BoundingBoxTable
): LuaTile[] {
return surface?.find_tiles_filtered({
area: area,
// collision_mask: ["ground-tile", "water-tile"]
handleScriptSetTilesEvent(event: ScriptRaisedSetTilesEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
return
}
const groupedTiles = MapChunkUpdateHandler.groupTiles(event.tiles)
for (const [chunkPos, tiles] of groupedTiles) {
MapChunkUpdateHandler.mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
}
}
handlePreChunkDeleted(event: OnPreChunkDeletedEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
return
}
for (const position of event.positions) {
const key: CacheKey<KafkatorioPacketData.MapChunkTileUpdate> = {
surfaceIndex: surface.index,
chunkPosition: [position.x, position.y],
}
) ?? []
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key,
Type.MapChunkTileUpdate,
data => data.isDeleted = true,
0, // deletion is important - emit ASAP
)
}
}
private static mapTilesUpdateDebounce(
surface: LuaSurface | undefined,
chunkPosition: MapChunkPosition,
tiles: TileRead[],
event: MapChunkUpdateEvent | MapTileChangeEvent,
updater?: MapChunkTileUpdater,
expirationDurationTicks?: uint,
) {
if (surface == undefined) {
return
}
const eventName = Converters.eventNameString(event.name)
const key: KafkatorioPacketData.MapChunkTileUpdate["key"] = {
surfaceIndex: surface.index,
chunkPosition: chunkPosition,
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key,
Type.MapChunkTileUpdate,
data => {
// tile dictionary update
if (data.tileDictionary == undefined) {
data.tileDictionary = {
tilesXY: {},
protos: {},
}
}
let protosCount: uint = table_size(data.tileDictionary.protos)
for (const tile of tiles) {
const protoId = Converters.prototypeId("tile", tile.name)
if (data.tileDictionary.protos[protoId] == null) {
}
data.tileDictionary.protos[protoId] ??= protosCount++ as PrototypeKey
const protoKey = data.tileDictionary.protos[protoId]
const xString = `${tile.position.x}`
const yString = `${tile.position.y}`
data.tileDictionary.tilesXY[xString] ??= {}
data.tileDictionary.tilesXY[xString][yString] = protoKey
}
// events count update
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
// apply mutator
if (updater != undefined) {
updater(data)
}
},
expirationDurationTicks,
)
}
private static groupTiles(tiles: TileRead[]): Map<MapChunkPosition, TileRead[]> {
const mapChunkPositionToTiles = new Map<MapChunkPosition, TileRead[]>()
for (const tile of tiles) {
const chunkPosition: MapChunkPosition = [
math.floor((tile.position.x / 32)),
math.floor((tile.position.y / 32)),
]
if (!mapChunkPositionToTiles.has(chunkPosition)) {
mapChunkPositionToTiles.set(chunkPosition, [])
}
mapChunkPositionToTiles.get(chunkPosition)?.push(tile)
}
return mapChunkPositionToTiles
}
}
function getSurface(surfaceIndex: uint): LuaSurface | undefined {
return game.surfaces[surfaceIndex]
}
const MapChunkUpdate = new MapChunkUpdateHandler()
export default MapChunkUpdate
script.on_event(
defines.events.on_chunk_generated,
(e: OnChunkGeneratedEvent) => {
log(`on_chunk_generated ${e.tick}`)
handleChunkGeneratedEvent(e)
MapChunkUpdate.handleChunkGeneratedEvent(e)
EntityUpdates.handleChunkGeneratedEvent(e)
}
)
export function handleChunkGeneratedEvent(
event: OnChunkGeneratedEvent,
expirationDurationTicks?: uint,
) {
let tiles = getTiles(event.surface, event.area)
mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
event,
undefined,
expirationDurationTicks,
)
}
// script.on_event(
// defines.events.on_chunk_charted,
// (e: OnChunkChartedEvent) => {
@ -144,169 +285,38 @@ export function handleChunkGeneratedEvent(
// )
script.on_event(
defines.events.script_raised_set_tiles,
(event: ScriptRaisedSetTilesEvent) => {
log(`script_raised_set_tiles ${event.tick}`)
const surface = getSurface(event.surface_index)
if (surface == undefined) {
return
}
const groupedTiles = groupTiles(event.tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
}
}
)
function groupTiles(tiles: TileRead[]): Map<MapChunkPosition, TileRead[]> {
const mapChunkPositionToTiles = new Map<MapChunkPosition, TileRead[]>()
for (const tile of tiles) {
const chunkPosition: MapChunkPosition = [
math.floor((tile.position.x / 32)),
math.floor((tile.position.y / 32)),
]
if (!mapChunkPositionToTiles.has(chunkPosition)) {
mapChunkPositionToTiles.set(chunkPosition, [])
}
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1256
mapChunkPositionToTiles.get(chunkPosition)?.push(tile)
// const tiles = mapChunkPositionToTiles.get(chunkPosition)
// if (tiles != null) {
// tiles.push(tile)
// }
}
return mapChunkPositionToTiles
}
script.on_event(defines.events.script_raised_set_tiles, (event: ScriptRaisedSetTilesEvent) => {
log(`script_raised_set_tiles ${event.tick}`)
MapChunkUpdate.handleScriptSetTilesEvent(event)
})
script.on_event(defines.events.on_player_built_tile, (e: OnPlayerBuiltTileEvent) => {
log(`on_player_built_tile ${e.tick}`)
handleBuiltTileEvent(e)
MapChunkUpdate.handleBuiltTileEvent(e)
})
script.on_event(defines.events.on_robot_built_tile, (e: OnRobotBuiltTileEvent) => {
log(`on_robot_built_tile ${e.tick}`)
handleBuiltTileEvent(e)
MapChunkUpdate.handleBuiltTileEvent(e)
})
function handleBuiltTileEvent(event: OnPlayerBuiltTileEvent | OnRobotBuiltTileEvent) {
const surface = getSurface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleBuiltTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertPlacedTiles(event.tile, event.tiles)
const groupedTiles = groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_built_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_built_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
name: event.robot.name,
protoType: event.robot.type,
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
script.on_event(defines.events.on_player_mined_tile, (e: OnPlayerMinedTileEvent) => {
log(`on_player_mined_tile ${e.tick}`)
handleMinedTileEvent(e)
MapChunkUpdate.handleMinedTileEvent(e)
EntityUpdates.handleMinedTileEvent(e)
})
script.on_event(defines.events.on_robot_mined_tile, (e: OnRobotMinedTileEvent) => {
log(`on_robot_mined_tile ${e.tick}`)
handleMinedTileEvent(e)
MapChunkUpdate.handleMinedTileEvent(e)
})
function handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = getSurface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleMinedTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertRemovedTiles(surface, event.tiles)
const groupedTiles = groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_mined_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_mined_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
name: event.robot.name,
protoType: event.robot.type,
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
script.on_event(
defines.events.on_pre_chunk_deleted,
(e: OnPreChunkDeletedEvent) => {
log(`on_pre_chunk_deleted ${e.tick}`)
const surface = getSurface(e.surface_index)
if (surface == undefined) {
return
}
for (const position of e.positions) {
const key: CacheKey<KafkatorioPacketData.MapChunkUpdate> = {
surfaceIndex: surface.index,
chunkPosition: [position.x, position.y],
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkUpdate>(
key,
Type.MapChunkUpdate,
data => data.isDeleted = true,
0, // deletion is important - emit ASAP
)
}
}
)
script.on_event(defines.events.on_pre_chunk_deleted, (e: OnPreChunkDeletedEvent) => {
log(`on_pre_chunk_deleted ${e.tick}`)
MapChunkUpdate.handlePreChunkDeleted(e)
})

View file

@ -1,11 +1,8 @@
import {Converters} from "./converters";
import EventUpdatesManager from "../cache/EventDataCache";
import {
KafkatorioPacketData,
PlayerUpdateKey
} from "../../generated/kafkatorio-schema";
import Type = KafkatorioPacketData.Type;
import {ForceIndex, KafkatorioPacketData, PlayerUpdateKey} from "../../generated/kafkatorio-schema";
import packetEmitter from "../PacketEmitter";
import Type = KafkatorioPacketData.Type;
type PlayerUpdater = (player: LuaPlayer, data: KafkatorioPacketData.PlayerUpdate) => void
@ -36,7 +33,7 @@ function playerUpdateThrottle(
const eventName = Converters.eventNameString(event.name)
EventUpdatesManager.throttle<KafkatorioPacketData.PlayerUpdate>(
{index: playerIndex},
{index: playerIndex },
Type.PlayerUpdate,
data => {
const player = game.players[playerIndex]
@ -88,7 +85,7 @@ script.on_event(
data.characterUnitNumber = player.character?.unit_number ?? null
data.chatColour = Converters.mapColour(player.chat_color)
data.colour = Converters.mapColour(player.color)
data.forceIndex = player.force.index
data.forceIndex = player.force.index as ForceIndex
data.name = player.name
data.isShowOnMap = player.show_on_map
data.isSpectator = player.spectator
@ -139,8 +136,7 @@ script.on_event(
if (e.cause != undefined) {
data.diedCause = {
unitNumber: e.cause.unit_number ?? null,
name: e.cause.name,
protoType: e.cause.type
protoId: Converters.prototypeId(e.cause.type, e.cause.name),
}
}
}
@ -189,7 +185,7 @@ script.on_event(
log(`on_pre_player_left_game ${event.tick} ${event.name}`)
const playerUpdateKey: PlayerUpdateKey = {
index: event.player_index
index: event.player_index ,
}
const playerUpdate: KafkatorioPacketData.PlayerUpdate = {

View file

@ -26,11 +26,11 @@ function prototypeUpdates(): FactorioPrototype[] {
function getMapTilePrototypes(): FactorioPrototype.MapTile[] {
let tiles: FactorioPrototype.MapTile[] = []
for (let [, tile] of game.tile_prototypes) {
for (const [, tile] of game.tile_prototypes) {
tiles.push({
type: FactorioPrototype.Type.MapTile,
name: tile.name,
protoId: Converters.prototypeId("tile", tile.name),
order: tile.order,
layer: tile.layer,
collisionMasks: Converters.convertCollisionMaskToNames(tile.collision_mask),
@ -44,7 +44,7 @@ function getMapTilePrototypes(): FactorioPrototype.MapTile[] {
// function getEntityPrototypes(): FactorioPrototype.MapTile[] {
// let tiles: FactorioPrototype.MapTile[] = []
// for (let [, tile] of game.tile_prototypes) {
// for (const [, tile] of game.tile_prototypes) {
// tiles.push({
// type: FactorioPrototype.Type.MapTile,
//

View file

@ -1,9 +1,9 @@
import {Queue} from "../queue/queue";
import {isEventType} from "./eventTypeCheck";
import {Converters} from "./converters";
import {handleChunkGeneratedEvent} from "./mapChunkUpdates";
import {handleSurfaceUpdate} from "./surfaceUpdates";
import EventCacheService from "../cache/EventDataCache";
import MapChunkUpdate from "./mapChunkUpdates";
script.on_event(
@ -35,7 +35,7 @@ script.on_event(
let eName = Converters.eventNameString(event.name)
log(`[on_tick:${event.tick}] dequed event ${eName}, delay ${i}`)
handleChunkGeneratedEvent(event, i)
MapChunkUpdate.handleChunkGeneratedEvent(event, i)
i += 1
}
}

View file

@ -0,0 +1,99 @@
// import {isEventType} from "./eventTypeCheck";
// import {Converters} from "./converters";
// import EventUpdatesManager, {EventUpdates} from "../cache/EventDataCache";
// import {
// KafkatorioPacketData,
// MapChunkPosition,
// PrototypeKey,
// } from "../../generated/kafkatorio-schema";
// import CacheKey = EventUpdates.PacketKey;
// import Type = KafkatorioPacketData.Type;
//
//
// export class ResourceEntityUpdatesHandler {
//
//
// handleChunkGeneratedEvent(
// event: OnChunkGeneratedEvent,
// expirationDurationTicks?: uint,
// ) {
// const resourceEntities = this.getAreaResourceEntities(event.surface, event.area)
//
// mapTilesUpdateDebounce(
// event.surface,
// Converters.convertMapTablePosition(event.position),
// tiles,
// event,
// undefined,
// expirationDurationTicks,
// )
// }
//
//
// private resourceEntityUpdatesThrottle(
// surface: LuaSurface | undefined,
// chunkPosition: MapChunkPosition,
// tiles: TileRead[],
// event: MapTileUpdateEvent,
// updater?: MapChunkUpdater,
// expirationDurationTicks?: uint,
// ) {
//
// if (surface == undefined) {
// return
// }
//
// const eventName = Converters.eventNameString(event.name)
//
// const key: KafkatorioPacketData.ResourceEntitiesUpdate["key"] = {
// surfaceIndex: surface.index as SurfaceIndex,
// chunkPosition: chunkPosition,
// }
//
// EventUpdatesManager.throttle<KafkatorioPacketData.ResourceEntitiesUpdate>(
// key,
// Type.MapChunkUpdate,
// data => {
//
// // tile dictionary update
// if (data.tileDictionary == undefined) {
// data.tileDictionary = {
// tilesXY: {},
// protos: {},
// }
// }
//
// let protosCount: uint = table_size(data.tileDictionary.protos)
// for (const tile of tiles) {
// const tileName = tile.name as PrototypeIdMapTile
// const protoKey = protosCount++ as PrototypeKey
// data.tileDictionary.protos[tileName] ??= protoKey
//
// const xString = `${tile.position.x}`
// const yString = `${tile.position.y}`
//
// data.tileDictionary.tilesXY[xString] ??= {}
// data.tileDictionary.tilesXY[xString][yString] = protoKey
// }
//
// // events count update
// data.events ??= {}
// data.events[eventName] ??= []
// data.events[eventName].push(event.tick)
//
// // apply mutator
// if (updater != undefined) {
// updater(data)
// }
// },
// expirationDurationTicks,
// )
// }
//
// }
//
//
//
// const ResourceEntityUpdates = new ResourceEntityUpdatesHandler()
//
// export default ResourceEntityUpdates

View file

@ -13,7 +13,7 @@ export function handleSurfaceUpdate(
name: surface.name,
index: surface.index,
daytime: surface.daytime
daytime: surface.daytime,
}
PacketEmitter.emitInstantPacket(update)

View file

@ -6,7 +6,8 @@ export class KafkatorioSettingsConfig {
private static eventCacheExpirationDefaultSeconds: Record<KafkatorioKeyedPacketTypes, double> = {
"kafkatorio.packet.keyed.PlayerUpdate": 0.5,
"kafkatorio.packet.keyed.MapChunkUpdate": 30,
"kafkatorio.packet.keyed.MapChunkTileUpdate": 30,
"kafkatorio.packet.keyed.MapChunkEntityUpdate": 30,
"kafkatorio.packet.keyed.EntityUpdate": 5,
}
@ -21,7 +22,7 @@ export class KafkatorioSettingsConfig {
public initialiseSettings(data: Data): void {
for (const [type,] of pairs(KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds)) {
data.extend(
[ KafkatorioSettingsConfig.createDefaultCacheDuration(type)]
[KafkatorioSettingsConfig.createDefaultCacheDuration(type)]
)
}
}

View file

@ -1,6 +1,6 @@
import {KafkatorioPacketData} from "../generated/kafkatorio-schema";
export type EventName = keyof typeof defines.events
export type DefinedEventName = keyof typeof defines.events
/** Get all keys of `TYPE` that are not nullable. */