mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 23:54:53 +09:00
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>
102 lines
4.0 KiB
Lua
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)
|