Files
factorio-expcluster/exp_statistics/test/module/env.lua
T
bbassieandClaude Opus 5.5 d1b22dd4e7 Add exp_statistics plugin for player statistics
Player statistics move out of exp_legacy into their own plugin, with the
totals kept on the controller instead of the PlayerData datastore, which
was never synced anywhere.

The lua module tracks the same statistics with the same rules. Gains are
kept per player as unsent in storage and sent over IPC every 5 minutes
for every player with changes, and for one player when they leave. Sent
values move into the totals the controller gave when the player joined,
so get() is the total on every server either way.

Nothing is resent. The instance plugin sends everything left when the
server stops and drops anything unsent when it starts. A leftover can
only come from a save made before it was sent, such as the autosave
loaded after a crash, so dropping it loses a few minutes at worst but
never counts twice.

The controller adds the deltas to a record per player name in
database/exp_statistics/players.json. Instances fetch a player's totals
on join and hand them to lua over rcon. The web UI gets a sortable table
of every player at /exp_statistics, behind exp_statistics.view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:44:59 +00:00

102 lines
4.0 KiB
Lua

--[[-- Test environment for the statistics module
Extends the shared stubs with fresh copies of module/control.lua and
module/events.lua, stubs of the exp_util functions they use, and players with
the fields the trackers read.
Run as a chunk by test/lua/runner.js, receiving the shared folder. The suite
returned here becomes `...` in each test file.
]]
local shared_root = ... --- @type string
local source = assert(debug.getinfo(1, "S")).source:gsub("\\", "/")
local plugin_root = assert(source:match("^@(.*)/test/module/env%.lua$"))
local Framework = assert(loadfile(shared_root .. "/framework.lua"))() --- @type Framework
--- Every event the module listens to, ids start after the shared stubs
local event_names = {
"on_player_left_game", "on_player_created", "on_player_changed_position",
"on_marked_for_deconstruction", "on_player_mined_entity", "on_entity_damaged",
"on_entity_died", "on_rocket_launched", "on_research_finished",
"on_built_entity", "on_chart_tag_added", "on_console_chat", "on_console_command",
"on_picked_up_item", "on_player_built_tile", "on_player_crafted_item",
"on_player_deconstructed_area", "on_player_died", "on_player_mined_tile",
"on_player_used_capsule", "on_player_repaired_entity",
}
--- @class ExpStatistics.TestEnv : Stubs
--- @field Statistics ExpStatistics A fresh copy of the statistics module
--- @field handlers { events: table<number, fun(event: table)>, on_nth_tick: table<number, fun()> } From events.lua
--- @field force fun(name: string): table A force stub with the online players added to it
--- @field raise fun(name: string, event: table) Call the handler of an event by its name
--- @field add_player fun(params: ExpStatistics.TestPlayerParams): ExpStatistics.TestPlayer
--- @class ExpStatistics.TestPlayerParams : Stubs.PlayerParams
--- @field afk_time number? Defaults to 0
--- @field force string? Defaults to "player"
--- @class ExpStatistics.TestPlayer : Stubs.Player
--- @field afk_time number
--- @field force table
return Framework.suite(function(env)
--- @cast env ExpStatistics.TestEnv
local events = {}
for index, name in ipairs(event_names) do
events[name] = 10 + index
end
env.extend_defines{ events = events }
env.extend_requires{
["modules/exp_util"] = {
format_time_factory_locale = function(opts)
return function(value) return { "time-" .. opts.format, value } end
end,
},
["modules/exp_util/storage"] = {
register = function() end,
},
["util"] = {
format_number = function(value) return tostring(value) end,
},
}
env.extend_requires{
["modules/exp_statistics/config"] = assert(loadfile(plugin_root .. "/module/config.lua"))(),
}
env.Statistics = assert(loadfile(plugin_root .. "/module/control.lua"))() --- @type ExpStatistics
env.extend_requires{ ["modules/exp_statistics/control"] = env.Statistics }
env.handlers = assert(loadfile(plugin_root .. "/module/events.lua"))()
local forces = {}
function env.force(name)
local force = forces[name]
if not force then
force = { name = name, valid = true, connected_players = {} }
forces[name] = force
end
return force
end
local add_player = env.add_player
function env.add_player(params)
local player = add_player(params) --[[@as ExpStatistics.TestPlayer]]
local force = env.force(params.force or "player")
rawset(player, "afk_time", params.afk_time or 0)
rawset(player, "force", force)
rawset(player, "object_name", "LuaPlayer")
if player.connected then
force.connected_players[#force.connected_players + 1] = player
end
return player
end
function env.raise(name, event)
local id = (defines.events --[[@as table<string, number>]])[name]
local handler = env.handlers.events[id]
assert(handler, "no handler for " .. name)
handler(event)
end
return env
end)