mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
The plugin is now exp_player_statistics (@expcluster/player-statistics), with the module, global, permission, route and database directory renamed to match. The exp-statistics locale section keeps its name. Every statistic is tracked unconditionally, so the enable flags are gone from config.lua. Playtime and JoinCount are no longer counted in game: clusterio already keeps online time and joins for each user, so the controller fills them in from the user when a player's totals are requested, and the web page does the same from the user list. They are not stored or adjusted by the plugin. The web table has a column picker, a multiple select with check boxes, Select all and Reset buttons, showing "N statistics selected". The choice is kept in local storage. The user page shows the statistics of the player in a grid like the core player stats. The readme statistics table drops its second title and uses the short time format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import type { ControllerPluginContext } from "@clusterio/controller";
|
|
import * as lib from "@clusterio/lib";
|
|
import * as messages from "./messages.js";
|
|
import * as path from "node:path";
|
|
|
|
/** Keeps the statistics totals of every player, instances send what was gained since their last update. */
|
|
export default async function (context: ControllerPluginContext) {
|
|
const { controller, plugin } = context;
|
|
const databaseDirectory = controller.config.get("controller.database_directory");
|
|
const players = new lib.SubscribableDatastore(
|
|
...await new lib.JsonIdDatastoreProvider(
|
|
path.join(databaseDirectory, "exp_player_statistics", "players.json"),
|
|
messages.PlayerStatistics.fromJSON.bind(messages.PlayerStatistics),
|
|
).bootstrap()
|
|
);
|
|
|
|
function addDeltas(deltas: messages.StatisticsDelta[]) {
|
|
const changed = new Map<string, messages.PlayerStatistics>();
|
|
for (const delta of deltas) {
|
|
const record = changed.get(delta.name)
|
|
?? players.getMutable(delta.name)
|
|
?? new messages.PlayerStatistics(delta.name);
|
|
for (const [name, value] of Object.entries(delta.stats)) {
|
|
if (Number.isFinite(value) && value !== 0) {
|
|
record.stats[name] = (record.stats[name] ?? 0) + value;
|
|
}
|
|
}
|
|
changed.set(record.id, record);
|
|
}
|
|
if (changed.size) {
|
|
players.setMany([...changed.values()]);
|
|
}
|
|
}
|
|
|
|
players.on("update", updates => {
|
|
controller.subscriptions.broadcast(new messages.PlayerStatisticsUpdatedEvent(updates));
|
|
});
|
|
|
|
controller.subscriptions.handle(messages.PlayerStatisticsUpdatedEvent, async request => {
|
|
const updates = [...players.values()].filter(player => player.updatedAtMs > request.lastRequestTimeMs);
|
|
return updates.length ? new messages.PlayerStatisticsUpdatedEvent(updates) : null;
|
|
});
|
|
|
|
controller.handle(messages.StatisticsUpdateEvent, async (event: messages.StatisticsUpdateEvent) => {
|
|
addDeltas(event.deltas);
|
|
});
|
|
|
|
controller.handle(messages.StatisticsGetRequest, async (request: messages.StatisticsGetRequest) => {
|
|
const record = players.get(request.name);
|
|
return new messages.PlayerStatistics(request.name, {
|
|
...record?.stats,
|
|
...messages.userStatistics(controller.users.getByName(request.name)),
|
|
}, record?.updatedAtMs);
|
|
});
|
|
|
|
controller.handle(messages.StatisticsListRequest, async () => [...players.values()]);
|
|
|
|
controller.hooks.save.attach(plugin.name, async () => {
|
|
await players.save();
|
|
});
|
|
controller.hooks.shutdown.attach(plugin.name, async () => {
|
|
await players.save();
|
|
});
|
|
}
|