Compare commits
2 Commits
main
...
send-rewri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0abc89d730 | ||
|
|
2e3ea7dd4d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
factorio-metrics-exporter_0.1.3.zip
|
||||
factorio-metrics-exporter_*.zip
|
||||
.vscode/
|
||||
120
control.lua
120
control.lua
@@ -6,20 +6,26 @@ require("power-stats")
|
||||
require("logistic-network-stats")
|
||||
require("train-stats")
|
||||
require("metrics-combinator")
|
||||
require("player-statistics")
|
||||
require("send-utils")
|
||||
|
||||
|
||||
tickInterval = tonumber(settings.global["factorio-metrics-exporter-tick-interval"].value) or 300
|
||||
udpAddress = 52555
|
||||
isInitialized = false
|
||||
sendIndex = 0
|
||||
serverIndex = 0
|
||||
scannedGrids = false
|
||||
scannedLabs = false
|
||||
scannedGenerators = false
|
||||
autotrainGroupName = ""
|
||||
autotrainDepotName = ""
|
||||
sendIndex = 0
|
||||
|
||||
|
||||
options = {
|
||||
---@type integer
|
||||
udpPort = 52555,
|
||||
---@type integer
|
||||
senderPlayerIndex = 0,
|
||||
---@type integer
|
||||
tickInterval = 300,
|
||||
enableMod = false,
|
||||
enablePlayers = false,
|
||||
enableProduction = false,
|
||||
@@ -35,12 +41,8 @@ options = {
|
||||
|
||||
|
||||
script.on_init(function()
|
||||
storage.electricGrids = {}
|
||||
storage.researchedTechnologies = {}
|
||||
storage.playerDeathCount = {}
|
||||
storage.totalLabCount = 0
|
||||
storage.totalResearchSpeed = 0
|
||||
storage.totalReseachProductivity = 0
|
||||
storage.labs = {}
|
||||
storage.playerKillCount = {}
|
||||
storage.representativePoles = {}
|
||||
@@ -58,16 +60,18 @@ script.on_init(function()
|
||||
storage.cargoStats = {}
|
||||
|
||||
storage.metrics = {}
|
||||
storage.cliffsDestroyed = 0
|
||||
|
||||
storage.nuclearReactorDeaths = 0
|
||||
storage.playersOnline = 0
|
||||
|
||||
|
||||
storage.scannedGrids = false
|
||||
storage.scannedLabs = false
|
||||
sendIndex = 0
|
||||
options.senderPlayerIndex = 0
|
||||
|
||||
options.enableMod = settings.global["factorio-metrics-exporter-enable"].value
|
||||
options.senderPlayerIndex = settings.global["factorio-metrics-exporter-sending-player-index"].value
|
||||
options.udpPort = settings.global["factorio-metrics-exporter-udp-port"].value
|
||||
options.enableProduction = settings.global["factorio-metrics-exporter-export_production_stats"].value
|
||||
options.enablePollution = settings.global["factorio-metrics-exporter-export_pollution_stats"].value
|
||||
options.enableFluid = settings.global["factorio-metrics-exporter-export_fluid_stats"].value
|
||||
@@ -84,10 +88,12 @@ end)
|
||||
|
||||
script.on_load(function()
|
||||
log("factorio-metrics-exporter: on_load")
|
||||
log("tickInterval: " .. tickInterval)
|
||||
log("udpAddress: " .. udpAddress)
|
||||
log("tickInterval: " .. options.tickInterval)
|
||||
log("udpAddress: " .. options.udpPort)
|
||||
|
||||
options.enableMod = settings.global["factorio-metrics-exporter-enable"].value
|
||||
options.senderPlayerIndex = settings.global["factorio-metrics-exporter-sending-player-index"].value
|
||||
options.udpPort = settings.global["factorio-metrics-exporter-udp-port"].value
|
||||
options.enableProduction = settings.global["factorio-metrics-exporter-export_production_stats"].value
|
||||
options.enablePollution = settings.global["factorio-metrics-exporter-export_pollution_stats"].value
|
||||
options.enableFluid = settings.global["factorio-metrics-exporter-export_fluid_stats"].value
|
||||
@@ -103,13 +109,9 @@ script.on_load(function()
|
||||
end)
|
||||
|
||||
script.on_configuration_changed(function()
|
||||
storage.electricGrids = storage.electricGrids or {}
|
||||
storage.labs = storage.labs or {}
|
||||
storage.playerDeathCount = storage.playerDeathCount or {}
|
||||
storage.researchedTechnologies = storage.researchedTechnologies or {}
|
||||
storage.totalLabCount = storage.totalLabCount or 0
|
||||
storage.totalReseachProductivity = storage.totalReseachProductivity or 0
|
||||
storage.totalResearchSpeed = storage.totalResearchSpeed or 0
|
||||
storage.playerKillCount = storage.playerKillCount or {}
|
||||
storage.representativePoles = storage.representativePoles or {}
|
||||
storage.scannedGrids = storage.scannedGrids or false
|
||||
@@ -127,7 +129,7 @@ script.on_configuration_changed(function()
|
||||
storage.metrics = storage.metrics or {}
|
||||
storage.cargoStats = storage.cargoStats or {}
|
||||
storage.nuclearReactorDeaths = storage.nuclearReactorDeaths or 0
|
||||
storage.cliffsDestroyed = storage.cliffsDestroyed or 0
|
||||
storage.playersOnline = storage.playersOnline or 0
|
||||
ScanNetworks()
|
||||
ScanLabs()
|
||||
ScanTrains()
|
||||
@@ -139,12 +141,24 @@ end
|
||||
|
||||
script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
|
||||
log("Mod setting changed: " .. event.setting)
|
||||
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-tick-interval" then
|
||||
tickInterval = settings.global["factorio-metrics-exporter-tick-interval"].value
|
||||
options.tickInterval = settings.global["factorio-metrics-exporter-tick-interval"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-enable" then
|
||||
options.enableMod = settings.global["factorio-metrics-exporter-enable"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-sending-player-index" then
|
||||
options.senderPlayerIndex = settings.global["factorio-metrics-exporter-sending-player-index"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-udp-port" then
|
||||
options.udpPort = settings.global["factorio-metrics-exporter-sending-player-index"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-export_production_stats" then
|
||||
options.enableProduction = settings.global["factorio-metrics-exporter-export_production_stats"].value
|
||||
end
|
||||
@@ -176,15 +190,19 @@ script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
|
||||
if event.setting == "factorio-metrics-exporter-export_research_stats" then
|
||||
options.enableResearch = settings.global["factorio-metrics-exporter-export_research_stats"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-export_train_stats" then
|
||||
options.enableTrains = settings.global["factorio-metrics-exporter-export_train_stats"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-autotrain_depot_name" then
|
||||
autotrainDepotName = settings.global["factorio-metrics-exporter-autotrain_depot_name"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-autotrain_group_name" then
|
||||
autotrainGroupName = settings.global["factorio-metrics-exporter-autotrain_group_name"].value
|
||||
end
|
||||
|
||||
if event.setting == "factorio-metrics-exporter-export_train_trips" then
|
||||
options.enableTrainTrips = settings.global["factorio-metrics-exporter-export_train_trips"].value
|
||||
end
|
||||
@@ -192,41 +210,11 @@ end)
|
||||
|
||||
|
||||
script.on_event(defines.events.on_player_died, function(event)
|
||||
--Log player cause by player
|
||||
if event.cause and event.cause.type == "character" then
|
||||
local killer = event.cause.player
|
||||
if killer then
|
||||
local killer_index = killer.index
|
||||
local victim_index = event.player_index
|
||||
local killerName = killer.name
|
||||
local victimName = game.players[victim_index].name
|
||||
log(("Player ID %d:%s killed player ID %d:%s"):format(killer_index, killerName, victim_index, victimName))
|
||||
|
||||
storage.playerKillCount[killer_index] =
|
||||
storage.playerKillCount[killer_index] or {}
|
||||
|
||||
storage.playerKillCount[killer_index][victim_index] =
|
||||
(storage.playerKillCount[killer_index][victim_index] or 0) + 1
|
||||
end
|
||||
end
|
||||
--Log cause of player death
|
||||
if event.cause and event.cause.name then
|
||||
storage.playerDeathCause[event.player_index] =
|
||||
storage.playerDeathCause[event.player_index] or {}
|
||||
|
||||
storage.playerDeathCause[event.player_index][event.cause.name] =
|
||||
(storage.playerDeathCause[event.player_index][event.cause.name] or 0) + 1
|
||||
|
||||
log(("Player %s died from type %s"):format(game.players[event.player_index].name, event.cause.name))
|
||||
end
|
||||
--Log player death count
|
||||
storage.playerDeathCount[event.player_index] = (storage.playerDeathCount[event.player_index] or 0) + 1
|
||||
|
||||
onPlayerKilledPlayer(event)
|
||||
onPlayerDiedDeathCause(event)
|
||||
onPlayerDeath(event)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
function SendGameStats()
|
||||
if options.enablePlayers then
|
||||
local returnParts = {}
|
||||
@@ -240,7 +228,7 @@ function SendGameStats()
|
||||
returnParts[#returnParts + 1] = GetPlayerDeaths()
|
||||
returnParts[#returnParts + 1] = GetPlayerDeathCauses()
|
||||
returnParts[#returnParts + 1] = GetPlayerKills()
|
||||
helpers.send_udp(udpAddress, table.concat(returnParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(returnParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -274,7 +262,7 @@ function SendAll(event)
|
||||
end
|
||||
|
||||
if options.enableMod == true then
|
||||
local interval = math.max(1, math.floor(tickInterval / 10))
|
||||
local interval = math.max(1, math.floor(options.tickInterval / 10))
|
||||
if event.tick % interval ~= 0 then return end
|
||||
sendIndex = (sendIndex % 10) + 1
|
||||
if sendIndex == 1 then SendProductionStats() end
|
||||
@@ -311,6 +299,7 @@ function UpdateStorage(event)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RemoveStorage(event)
|
||||
if not event then return end
|
||||
if event.entity.type == "lab" then
|
||||
@@ -325,11 +314,8 @@ function RemoveStorage(event)
|
||||
RemoveGenerator(event)
|
||||
end
|
||||
|
||||
--log(event.entity.name)
|
||||
if event.entity.name == "crash-site-spaceship" then
|
||||
--log(event.name)
|
||||
if event.name == defines.events.on_player_mined_entity then
|
||||
--log("in ban call")
|
||||
if settings.global["factorio-metrics-exporter-enable_denkmalschutz"].value == true then
|
||||
game.ban_player(event.player_index, "You violated the rules of DENKMALSCHUTZ!!!")
|
||||
game.kick_player(event.player_index, "")
|
||||
@@ -343,10 +329,11 @@ function RemoveStorage(event)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function CreateEntity(event)
|
||||
if not event then return end
|
||||
|
||||
|
||||
--Event is PlayerPlaced
|
||||
if event.name == defines.events.on_built_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
@@ -358,7 +345,6 @@ function CreateEntity(event)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--Event is RobotPlaced
|
||||
if event.name == defines.events.on_robot_built_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
@@ -373,7 +359,6 @@ function CreateEntity(event)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--Event is spaceplatform build
|
||||
if event.name == defines.events.on_space_platform_built_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
@@ -395,7 +380,7 @@ function RemoveEntity(event)
|
||||
if event.name == defines.events.on_player_mined_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
and event.entity.name ~= "tile-ghost"
|
||||
and event.entity.name ~= "deconstructible_tile_proxy" then
|
||||
and event.entity.name ~= "deconstructible-tile-proxy" then
|
||||
storage.deconstructedEntities[event.player_index] = storage.deconstructedEntities[event.player_index] or {}
|
||||
storage.deconstructedEntities[event.player_index][event.entity.name] = (storage.deconstructedEntities[event.player_index][event.entity.name] or 0) +
|
||||
1
|
||||
@@ -405,7 +390,7 @@ function RemoveEntity(event)
|
||||
if event.name == defines.events.on_robot_mined_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
and event.entity.name ~= "tile-ghost"
|
||||
and event.entity.name ~= "deconstructible_tile_proxy" then
|
||||
and event.entity.name ~= "deconstructible-tile-proxy" then
|
||||
if event.entity.last_user then
|
||||
local lastUser = event.entity.last_user.index
|
||||
storage.deconstructedEntities[lastUser] = storage.deconstructedEntities[lastUser] or {}
|
||||
@@ -418,7 +403,7 @@ function RemoveEntity(event)
|
||||
if event.name == defines.events.on_space_platform_mined_entity then
|
||||
if event.entity.name ~= "entity-ghost"
|
||||
and event.entity.name ~= "tile-ghost"
|
||||
and event.entity.name ~= "deconstructible_tile_proxy" then
|
||||
and event.entity.name ~= "deconstructible-tile-proxy" then
|
||||
if event.entity.last_user then
|
||||
local lastUser = event.entity.last_user.index
|
||||
storage.deconstructedEntities[lastUser] = storage.deconstructedEntities[lastUser] or {}
|
||||
@@ -429,8 +414,9 @@ function RemoveEntity(event)
|
||||
end
|
||||
|
||||
if event.name == defines.events.on_entity_died then
|
||||
end
|
||||
CheckReactor(event)
|
||||
end
|
||||
|
||||
RemoveStorage(event)
|
||||
onMetricsCombinatorDied(event)
|
||||
onMetricsCombinatorMined(event)
|
||||
@@ -438,13 +424,6 @@ end
|
||||
|
||||
script.on_event(defines.events.on_tick, SendAll)
|
||||
|
||||
--Script hooks for power and lab stats
|
||||
--script.on_event(defines.events.on_built_entity,UpdateStorage,{{filter = "type", type = "electric-pole"},{filter ="type", type="lab"}})
|
||||
--script.on_event(defines.events.on_player_mined_entity, RemoveStorage,{{filter = "type", type = "electric-pole"},{filter ="type", type="lab"},{filter = "type", type="container"}})
|
||||
--script.on_event(defines.events.on_robot_built_entity,UpdateStorage,{{filter = "type", type = "electric-pole"},{filter ="type", type="lab"}})
|
||||
--script.on_event(defines.events.on_robot_mined_entity,RemoveStorage,{{filter = "type", type = "electric-pole"},{filter ="type", type="lab"},{filter = "type", type="container"}})
|
||||
--script.on_event(defines.events.on_entity_died,RemoveStorage,{{filter = "type", type = "electric-pole"},{filter ="type", type="lab"},{filter = "type", type="container"}})
|
||||
|
||||
script.on_event(defines.events.on_built_entity, CreateEntity)
|
||||
script.on_event(defines.events.on_robot_built_entity, CreateEntity)
|
||||
script.on_event(defines.events.on_space_platform_built_entity, CreateEntity)
|
||||
@@ -459,6 +438,3 @@ script.on_event(defines.events.on_gui_opened, onGuiOpened)
|
||||
script.on_event(defines.events.on_gui_click, onGuiClick)
|
||||
script.on_event(defines.events.on_gui_closed, onClosedCombinatorGui)
|
||||
script.on_event(defines.events.on_gui_text_changed, onGuiTextChanged)
|
||||
|
||||
script.on_event(defines.events.on_cargo_pod_delivered_cargo, onCargoDelivered)
|
||||
script.on_event(defines.events.on_rocket_launched, onCargoDelivered)
|
||||
|
||||
@@ -5,7 +5,7 @@ function GetMods()
|
||||
for k, v in pairs(mods) do
|
||||
modstring = modstring .. ("%s:%s\n"):format(k, v)
|
||||
end
|
||||
helpers.send_udp(udpAddress, modstring, serverIndex)
|
||||
SendChunked(modstring)
|
||||
end
|
||||
|
||||
function GetPlayerColors()
|
||||
@@ -31,26 +31,38 @@ function GetPlayerKills()
|
||||
end
|
||||
return table.concat(killParts, "\n")
|
||||
end
|
||||
|
||||
function SendPlayerEntityStats()
|
||||
local entityParts = {}
|
||||
entityParts[#entityParts + 1] = "---player-build-stats---"
|
||||
local resultParts = {}
|
||||
-- Localize for speed
|
||||
local insert = table.insert
|
||||
|
||||
local prof = game.create_profiler()
|
||||
|
||||
insert(resultParts, "---player-build-stats---")
|
||||
|
||||
-- Process Constructed
|
||||
for playerIndex, items in pairs(storage.constructedEntites) do
|
||||
local playerName = game.players[playerIndex].name
|
||||
local prefix = playerIndex .. ":" .. playerName .. ":c:"
|
||||
|
||||
for itemName, itemCount in pairs(items) do
|
||||
entityParts[#entityParts + 1] = ("%s:%s:constructed:%s:%s"):format(playerIndex, playerName, itemName, itemCount)
|
||||
insert(resultParts, prefix .. itemName .. ":" .. itemCount)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress,table.concat(entityParts,"\n"),serverIndex)
|
||||
entityParts = {}
|
||||
entityParts[#entityParts + 1] = "---player-build-stats---"
|
||||
|
||||
-- Process Deconstructed
|
||||
for playerIndex, items in pairs(storage.deconstructedEntities) do
|
||||
local playerName = game.players[playerIndex].name
|
||||
local prefix = playerIndex .. ":" .. playerName .. ":d:"
|
||||
|
||||
for itemName, itemCount in pairs(items) do
|
||||
entityParts[#entityParts + 1] = ("%s:%s:deconstructed:%s:%s"):format(playerIndex, playerName, itemName, itemCount)
|
||||
insert(resultParts, prefix .. itemName .. ":" .. itemCount)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress,table.concat(entityParts,"\n"),serverIndex)
|
||||
|
||||
prof.stop()
|
||||
game.print(prof)
|
||||
SendChunked(table.concat(resultParts, "\n"))
|
||||
end
|
||||
|
||||
function GetMapSeed()
|
||||
@@ -65,40 +77,40 @@ end
|
||||
---Takes all players that ever visited the server into account
|
||||
---@return string
|
||||
function GetPlayerTime()
|
||||
local timeParts = {}
|
||||
timeParts[#timeParts + 1] = "---player-times---\n"
|
||||
local resultParts = {}
|
||||
resultParts[#resultParts + 1] = "---player-times---\n"
|
||||
for _, player in pairs(game.players) do
|
||||
timeParts[#timeParts + 1] = ("%s:%d:%d"):format(player.name, player.index, player.online_time)
|
||||
resultParts[#resultParts + 1] = ("%s:%d:%d"):format(player.name, player.index, player.online_time)
|
||||
end
|
||||
return table.concat(timeParts, "\n")
|
||||
return table.concat(resultParts, "\n")
|
||||
end
|
||||
|
||||
---comment
|
||||
---@return string
|
||||
function GetPlayerDeaths()
|
||||
local deathParts = {}
|
||||
deathParts[#deathParts + 1] = "---player-deaths---\n"
|
||||
local resultParts = {}
|
||||
resultParts[#resultParts + 1] = "---player-deaths---\n"
|
||||
for _, player in pairs(game.players) do
|
||||
deathParts[#deathParts + 1] = ("%s:%d:%d"):format(player.name, player.index,
|
||||
resultParts[#resultParts + 1] = ("%s:%d:%d"):format(player.name, player.index,
|
||||
storage.playerDeathCount[player.index] or 0)
|
||||
end
|
||||
return table.concat(deathParts, "\n")
|
||||
return table.concat(resultParts, "\n")
|
||||
end
|
||||
|
||||
function onPlayerDeath(event)
|
||||
helpers.send_udp(udpAddress,("---player-died---\n%s:%s:%d"):format(event.player_index,game.players[event.player_index].name,event.tick),serverIndex)
|
||||
SendChunked(("---player-died---\n%s:%s:%d"):format(event.player_index,game.players[event.player_index].name,event.tick))
|
||||
end
|
||||
|
||||
function GetPlayerDeathCauses()
|
||||
local deathParts = {}
|
||||
deathParts[#deathParts + 1] = "---player-death-cause---\n"
|
||||
local resultParts = {}
|
||||
resultParts[#resultParts + 1] = "---player-death-cause---\n"
|
||||
for playerIndex, deathCauses in pairs(storage.playerDeathCause) do
|
||||
for causeName, causeCount in pairs(deathCauses) do
|
||||
deathParts[#deathParts + 1] = ("%s:%d:%s:%d"):format(game.players[playerIndex].name, playerIndex, causeName,
|
||||
resultParts[#resultParts + 1] = ("%s:%d:%s:%d"):format(game.players[playerIndex].name, playerIndex, causeName,
|
||||
causeCount)
|
||||
end
|
||||
end
|
||||
return table.concat(deathParts, "\n")
|
||||
return table.concat(resultParts, "\n")
|
||||
end
|
||||
|
||||
function GetTotalPlayTime()
|
||||
|
||||
@@ -34,5 +34,7 @@ factorio-metrics-exporter-autotrain_depot_name=Set the name of the depot you wan
|
||||
factorio-metrics-exporter-export_train_trips=Enable sending of train trup statistics.
|
||||
[item-name]
|
||||
metrics-combinator=Metrics combinator
|
||||
[entity-name]
|
||||
metrics-combinator=Metrics combinator
|
||||
[item-description]
|
||||
metrics-combinator=Connect this item to a circuit network, set a name and check the enable checkbox to export the values of this circuit network
|
||||
@@ -39,6 +39,6 @@ function SendLogisticStats()
|
||||
log("Table size logistics "..table_size(returnParts))
|
||||
log("Sending logistics")
|
||||
--local send = GetAllLogisticGrids().."\n"..GetLogisticNetworkContents()
|
||||
helpers.send_udp(udpAddress,table.concat(returnParts,"\n"),serverIndex)
|
||||
SendChunked(table.concat(returnParts,"\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -158,8 +158,12 @@ function SendCombinatorMetrics()
|
||||
local greenNet = entity.get_circuit_network(defines.wire_connector_id.circuit_green)
|
||||
if redNet then
|
||||
for _, signal in pairs(redNet.signals) do
|
||||
local quality = "-normal"
|
||||
if signal.signal.quality then
|
||||
quality = signal.signal.quality.name
|
||||
end
|
||||
netParts[#netParts + 1] = ("%s:red:%s:%d"):format(combinatorFlags.name,
|
||||
signal.signal.name .. signal.signal.quality.name, signal.count)
|
||||
signal.signal.name.."-"..quality, signal.count)
|
||||
end
|
||||
end
|
||||
if greenNet then
|
||||
@@ -171,5 +175,5 @@ function SendCombinatorMetrics()
|
||||
end
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(netParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(netParts, "\n"))
|
||||
end
|
||||
|
||||
47
player-statistics.lua
Normal file
47
player-statistics.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
function onPlayerJoin(event)
|
||||
SendChunked(
|
||||
"---player-join---\n" ..
|
||||
("%d:%s"):format(event.player_index,game.players[event.player_index].name)
|
||||
)
|
||||
end
|
||||
|
||||
function onPlayerLeave(event)
|
||||
SendChunked(
|
||||
"---player-leave---\n" ..
|
||||
("%d:%s"):format(event.player_index,game.players[event.player_index].name)
|
||||
)
|
||||
end
|
||||
|
||||
function onPlayerDiedDeathCause(event)
|
||||
if event.cause and event.cause.name then
|
||||
storage.playerDeathCause[event.player_index] =
|
||||
storage.playerDeathCause[event.player_index] or {}
|
||||
|
||||
storage.playerDeathCause[event.player_index][event.cause.name] =
|
||||
(storage.playerDeathCause[event.player_index][event.cause.name] or 0) + 1
|
||||
log(("Player %s died from type %s"):format(game.players[event.player_index].name, event.cause.name))
|
||||
end
|
||||
|
||||
--Log player death count
|
||||
storage.playerDeathCount[event.player_index] = (storage.playerDeathCount[event.player_index] or 0) + 1
|
||||
end
|
||||
|
||||
function onPlayerKilledPlayer(event)
|
||||
--Log player cause by player
|
||||
if event.cause and event.cause.type == "character" then
|
||||
local killer = event.cause.player
|
||||
if killer then
|
||||
local killer_index = killer.index
|
||||
local victim_index = event.player_index
|
||||
local killerName = killer.name
|
||||
local victimName = game.players[victim_index].name
|
||||
log(("Player ID %d:%s killed player ID %d:%s"):format(killer_index, killerName, victim_index, victimName))
|
||||
|
||||
storage.playerKillCount[killer_index] =
|
||||
storage.playerKillCount[killer_index] or {}
|
||||
|
||||
storage.playerKillCount[killer_index][victim_index] =
|
||||
(storage.playerKillCount[killer_index][victim_index] or 0) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ function SendPollutionStats()
|
||||
pollutionParts[#pollutionParts + 1] = ("%s:out:%s:%d"):format(surface_name, name, stat)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(pollutionParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(pollutionParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ function SendKillStats()
|
||||
killParts[#killParts + 1] = ("%s:out:%s:%d"):format(surface_name, name, stat)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(killParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(killParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ function AddPowerPole(event)
|
||||
local e = event.entity
|
||||
if e then
|
||||
storage.representativePoles[e.unit_number] = e
|
||||
-- Update cache with new network
|
||||
-- Invalidate cache
|
||||
storage.networkCache = nil
|
||||
end
|
||||
end
|
||||
@@ -187,6 +187,7 @@ function SendPowerStats()
|
||||
end
|
||||
end
|
||||
powerPart[#powerPart + 1] = possiblePower
|
||||
helpers.send_udp(udpAddress, table.concat(powerPart, "\n"), serverIndex)
|
||||
--Send("---power-stats---",table.concat(powerPart,"\n"),200)
|
||||
SendChunked(table.concat(powerPart, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ function SendProductionStats()
|
||||
productionParts[#productionParts+1] = ("%s:out:%s:%d"):format(surfaceName, itemName, itemCount)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(productionParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(productionParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ function SendFluidProductionStats()
|
||||
productionParts[#productionParts+1] = ("%s:out:%s:%d"):format(surfaceName, itemName, itemCount)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(productionParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(productionParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -105,7 +105,7 @@ function SendBuildStats()
|
||||
buildParts[#buildParts+1] = ("%s:out:%s:%d"):format(surfaceName, itemName, itemCount)
|
||||
end
|
||||
end
|
||||
helpers.send_udp(udpAddress, table.concat(buildParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(buildParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -56,28 +56,6 @@ function GetEstimatedResearchTime()
|
||||
return returnSpeed..returnTime..returnNameID..returnProgress..returnCost
|
||||
end
|
||||
|
||||
function UpdateLabInfos()
|
||||
local totalLabs = 0
|
||||
local totalSpeed = 0
|
||||
totalLabs = #storage.labs
|
||||
for _, lab in pairs(storage.labs) do
|
||||
if lab.valid then
|
||||
if lab.status == defines.entity_status.working then
|
||||
local labBase
|
||||
if lab.name == "biolab" then
|
||||
labBase = biolabBaseSpeed
|
||||
else
|
||||
labBase = labBaseSpeed
|
||||
end
|
||||
local labSpeed = (labBase + (labBase * game.forces["player"].laboratory_speed_modifier)) * (lab.effects.speed or 1)
|
||||
totalSpeed = totalSpeed + (labSpeed* (1+(lab.effects.productivity or 0)))
|
||||
end
|
||||
end
|
||||
end
|
||||
storage.totalLabCount = totalLabs
|
||||
storage.totalResearchSpeed = totalSpeed
|
||||
end
|
||||
|
||||
function GetCurrentResearchSpeed()
|
||||
local totalResearch = 0
|
||||
local playerForce = game.forces["player"]
|
||||
@@ -92,7 +70,7 @@ function SendResearchStats()
|
||||
if options.enableResearch == true then
|
||||
local researchTimeInfo = GetEstimatedResearchTime()
|
||||
if researchTimeInfo then
|
||||
helpers.send_udp(udpAddress, researchTimeInfo, serverIndex)
|
||||
SendChunked(researchTimeInfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
94
send-utils.lua
Normal file
94
send-utils.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
local function SendUDP(content)
|
||||
if content then
|
||||
helpers.send_udp(options.udpPort, content, options.senderPlayerIndex)
|
||||
else
|
||||
error("Missing udpPort or Content")
|
||||
end
|
||||
end
|
||||
|
||||
---Chunks content into appropriately sized pieces, respecting header lines
|
||||
---Chunks content greedily to minimize UDP calls
|
||||
---Fills 200KB buffer as completely as possible, including multiple headers if they fit
|
||||
---Only prepends header when chunk starts mid-section
|
||||
---@return table chunks Array of {header=string|nil, content=string}---@param content string
|
||||
---@param maxSizekB number|nil Default 200KB
|
||||
local function ChunkContent(content, maxSizekB)
|
||||
local maxSize = (maxSizekB or 200) * 1024
|
||||
local len = #content
|
||||
local chunks = {}
|
||||
|
||||
-- 1. Pre-compute headers (O(N))
|
||||
local headers = {}
|
||||
local hPos = 1
|
||||
while true do
|
||||
local hStart, hEnd, hText = content:find("%%-%-%-(.-)%%-%-%-\n", hPos)
|
||||
if not hStart then break end
|
||||
table.insert(headers, {startPos = hStart, text = "---" .. hText .. "---"})
|
||||
hPos = hEnd + 1
|
||||
end
|
||||
|
||||
local start = 1
|
||||
local headerIdx = 1
|
||||
local activeHeaderText = nil
|
||||
|
||||
while start <= len do
|
||||
while headerIdx <= #headers and headers[headerIdx].startPos <= start do
|
||||
activeHeaderText = headers[headerIdx].text
|
||||
headerIdx = headerIdx + 1
|
||||
end
|
||||
|
||||
local isAtHeaderStart = (headerIdx > 1 and headers[headerIdx-1].startPos == start)
|
||||
local needsPrepend = (not isAtHeaderStart) and (activeHeaderText ~= nil)
|
||||
local overhead = needsPrepend and (#activeHeaderText + 1) or 0
|
||||
local availableSpace = maxSize - overhead
|
||||
|
||||
if availableSpace <= 0 then error("Header exceeds maxSize") end
|
||||
|
||||
local target = math.min(start + availableSpace - 1, len)
|
||||
|
||||
if target == len then
|
||||
table.insert(chunks, {header = needsPrepend and activeHeaderText or nil, content = content:sub(start, len)})
|
||||
break
|
||||
end
|
||||
|
||||
-- Optimized C-level search for newline
|
||||
local chunkView = content:sub(start, target)
|
||||
local lastNewline = chunkView:match(".*()\n")
|
||||
|
||||
if not lastNewline then
|
||||
error("Line too long at byte " .. start)
|
||||
end
|
||||
|
||||
local splitPos = start + lastNewline - 1
|
||||
table.insert(chunks, {header = needsPrepend and activeHeaderText or nil, content = content:sub(start, splitPos)})
|
||||
start = splitPos + 1
|
||||
end
|
||||
return chunks
|
||||
end
|
||||
|
||||
---Sends content in chunks with optional header
|
||||
---If content starts with header (---<headerline>---), it's automatically repeated in each chunk
|
||||
---@param content string
|
||||
---@param maxSizekB number|nil Default 200KB
|
||||
function SendChunked(content, maxSizekB)
|
||||
if not content or #content == 0 then
|
||||
error("Missing or empty content")
|
||||
end
|
||||
|
||||
local maxSize = (maxSizekB or 200) * 1024
|
||||
|
||||
-- Content is smaller than maxSize, so send immediately
|
||||
if #content <= maxSize then
|
||||
SendUDP(content)
|
||||
return
|
||||
end
|
||||
|
||||
local chunks = ChunkContent(content, maxSizekB)
|
||||
for _, chunk in ipairs(chunks) do
|
||||
if chunk.header then
|
||||
SendUDP(chunk.header .. "\n" .. chunk.content)
|
||||
else
|
||||
SendUDP(chunk.content)
|
||||
end
|
||||
end
|
||||
end
|
||||
10
settings.lua
10
settings.lua
@@ -17,7 +17,7 @@ data:extend({
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "factorio-metrics-exporter-udp-port",
|
||||
setting_type = "startup",
|
||||
setting_type = "runtime-global",
|
||||
allow_blank = false,
|
||||
default_value = 52555,
|
||||
order = "c"
|
||||
@@ -112,5 +112,13 @@ data:extend({
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "zz"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "factorio-metrics-exporter-sending-player-index",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 0,
|
||||
minimum_value = 0,
|
||||
order = "aa"
|
||||
}
|
||||
})
|
||||
@@ -272,13 +272,13 @@ function GetTrainTripStats()
|
||||
end
|
||||
if #tripParts > 400 then
|
||||
log("Sending at " .. tripCount .. " trips")
|
||||
helpers.send_udp(udpAddress, table.concat(tripParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(tripParts, "\n"))
|
||||
tripParts = {}
|
||||
tripParts[#tripParts + 1] = "---train-trips---\n"
|
||||
end
|
||||
end
|
||||
--tripParts[#tripParts+1] = "--train-fin--"
|
||||
helpers.send_udp(udpAddress, table.concat(tripParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(tripParts, "\n"))
|
||||
log("Counted " .. tripCount .. " trips")
|
||||
--return table.concat(tripParts,"\n")
|
||||
end
|
||||
@@ -326,6 +326,6 @@ function SendTrainStats()
|
||||
GetTrainTripStats()
|
||||
end
|
||||
log("Sending Train statistics")
|
||||
helpers.send_udp(udpAddress, table.concat(returnParts, "\n"), serverIndex)
|
||||
SendChunked(table.concat(returnParts, "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user