Files
BastiaanandClaude Opus 5.5 68a964cf57 Write plugins without state shared elsewhere as functions (#492)
The instance plugins of exp_groups, exp_reports, exp_roles, exp_scenario
and exp_server_ups, and the controller plugins of exp_reports and
exp_scenario, were classes only to hold their handlers. They are now
init functions with closures, like clusterio's own plugins.

The exp_roles and exp_groups controller plugins stay classes because
exp_scenario reaches their datastores through get(controller).
AutoRestart stays a class because its tests step check() with a fake
clock.

Tests spy on handle() and the hooks to call the handlers. The reports
webhook tests stub fetch instead of replacing postWebhook, and the
feature reconcile test writes the stored records to disk before start.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:50:15 +01:00

70 lines
2.0 KiB
TypeScript

import * as lib from "@clusterio/lib";
import type { InstancePluginContext } from "@clusterio/host";
export default async function (context: InstancePluginContext) {
const { instance, logger, plugin } = context;
let updateInterval: ReturnType<typeof setInterval> | undefined;
const gameTimes: number[] = [];
function startUpdates() {
if (!updateInterval) {
updateInterval = setInterval(updateUps, instance.config.get("exp_server_ups.update_interval"));
}
}
function stopUpdates() {
if (updateInterval) {
clearInterval(updateInterval);
updateInterval = undefined;
}
}
async function updateUps() {
let ups = 0;
const collected = gameTimes.length - 1;
if (collected > 0) {
const minTick = gameTimes[0];
const maxTick = gameTimes[collected];
const interval = instance.config.get("exp_server_ups.update_interval") / 1000;
ups = (maxTick - minTick) / (collected * interval);
}
try {
const newGameTime = await instance.sendRcon(`/_rcon return exp_server_ups.refresh(${ups})`, false, plugin.name);
gameTimes.push(Number(newGameTime));
} catch (error: any) {
logger.error(`Failed to receive new game time: ${error}`);
}
if (collected > instance.config.get("exp_server_ups.average_interval")) {
gameTimes.shift();
}
}
const hooks = instance.hooks;
hooks.start.attach(plugin.name, async () => {
if (!instance.config.get("factorio.settings")["auto_pause"]) {
startUpdates();
}
});
hooks.exit.attach(plugin.name, stopUpdates);
hooks.instanceConfigFieldChanged.attach(plugin.name, async (field, curr) => {
if (field === "exp_server_ups.update_interval") {
stopUpdates();
startUpdates();
} else if (field === "exp_server_ups.average_interval") {
gameTimes.splice(curr as number);
}
});
hooks.playerEvent.attach(plugin.name, async (event: lib.PlayerEvent) => {
if (event.type === "join") {
startUpdates();
} else if (event.type === "leave" && instance.playersOnline.size == 0 && instance.config.get("factorio.settings")["auto_pause"]) {
stopUpdates();
}
});
}