diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index 2377ef80..8d26c24e 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -661,7 +661,7 @@ export default class Controller { let instance = new InstanceInfo({ config: instanceConfig, status: "unassigned" }); this.instances!.set(instanceId, instance); - await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance, null); + await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance); this.addInstanceHooks(instance); return instance; } diff --git a/packages/controller/src/HostConnection.ts b/packages/controller/src/HostConnection.ts index 2054fe11..5d4f3b13 100644 --- a/packages/controller/src/HostConnection.ts +++ b/packages/controller/src/HostConnection.ts @@ -233,7 +233,7 @@ export default class HostConnection extends BaseConnection { instanceConfig.get("instance.id"), instanceConfig.serialize("host") ) ); - await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", newInstance, null); + await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", newInstance); } // Push lists to make sure they are in sync. diff --git a/packages/controller/src/routes.ts b/packages/controller/src/routes.ts index 1c9c441c..d5f9dfbe 100644 --- a/packages/controller/src/routes.ts +++ b/packages/controller/src/routes.ts @@ -37,8 +37,7 @@ async function getMetrics(req: Request, res: Response, next: any) { const controller: Controller = req.app.locals.controller; let results: lib.CollectorResult[] = []; - type ResultIterator = AsyncIterable | Iterable - let pluginResults = await lib.invokeHook(controller.plugins, "onMetrics") as ResultIterator[]; + let pluginResults = await lib.invokeHook(controller.plugins, "onMetrics"); for (let metricIterator of pluginResults) { for await (let metric of metricIterator) { results.push(metric); diff --git a/packages/host/src/Host.ts b/packages/host/src/Host.ts index 1c5a2e03..c170e1ad 100644 --- a/packages/host/src/Host.ts +++ b/packages/host/src/Host.ts @@ -594,8 +594,7 @@ export default class Host extends lib.Link { } let results = []; - type ResultIterator = AsyncIterable | Iterable - let pluginResults = await lib.invokeHook(this.plugins, "onMetrics") as ResultIterator[]; + let pluginResults = await lib.invokeHook(this.plugins, "onMetrics"); for (let metricIterator of pluginResults) { for await (let metric of metricIterator) { results.push(metric); @@ -859,7 +858,7 @@ export default class Host extends lib.Link { } async prepareDisconnect() { - await lib.invokeHook(this.plugins, "onPrepareControllerDisconnect"); + await lib.invokeHook(this.plugins, "onPrepareControllerDisconnect", this); for (let instanceConnection of this.instanceConnections.values()) { await instanceConnection.send(new lib.PrepareControllerDisconnectRequest()); } diff --git a/packages/host/src/Instance.ts b/packages/host/src/Instance.ts index a2e0d347..39945525 100644 --- a/packages/host/src/Instance.ts +++ b/packages/host/src/Instance.ts @@ -965,14 +965,13 @@ rcon.print(game.table_to_json(players))`.replace(/\r?\n/g, " "); } async handlePrepareControllerDisconnectRequest() { - await lib.invokeHook(this.plugins, "onPrepareControllerDisconnect"); + await lib.invokeHook(this.plugins, "onPrepareControllerDisconnect", this); } async handleInstanceMetricsRequest() { let results: ReturnType[] = []; if (!["stopped", "stopping"].includes(this._status)) { - type ResultIterator = AsyncIterable | Iterable - let pluginResults = await lib.invokeHook(this.plugins, "onMetrics") as ResultIterator[]; + let pluginResults = await lib.invokeHook(this.plugins, "onMetrics"); for (let metricIterator of pluginResults) { for await (let metric of metricIterator) { results.push(lib.serializeResult(metric)); diff --git a/packages/lib/src/data/messages_host.ts b/packages/lib/src/data/messages_host.ts index 92b5452f..bf91c5a4 100644 --- a/packages/lib/src/data/messages_host.ts +++ b/packages/lib/src/data/messages_host.ts @@ -96,11 +96,11 @@ export class ControllerConnectionEvent { static dst = "instance" as const; constructor( - public event: string, + public event: "connect" | "drop" | "resume" | "close", ) { } static jsonSchema = Type.Object({ - "event": Type.String(), + "event": StringEnum(["connect", "drop", "resume", "close"]), }); static fromJSON(json: Static) { diff --git a/packages/lib/src/plugin.ts b/packages/lib/src/plugin.ts index e6731afe..f9baea7d 100644 --- a/packages/lib/src/plugin.ts +++ b/packages/lib/src/plugin.ts @@ -3,6 +3,7 @@ * @module lib/plugin */ import * as libHelpers from "./helpers"; +import type { Logger } from "./logging"; import type { PluginConfigGroup } from "./config"; import type PlayerStats from "./PlayerStats"; @@ -71,14 +72,22 @@ export interface PlayerEvent { * @param args - Arguments to pass on to the hook. * @returns Non-undefined return values from the hooks. */ -export async function invokeHook(plugins: Map, hook: string, ...args: any[]) { - let results: unknown[] = []; +export async function invokeHook< + Hook extends string, + R, + Args extends [...any], + Plugin extends { logger: Logger } & Record R | Promise> +>( + plugins: Map, + hook: Hook, + ...args: Args +): Promise>, void>[]> { + let results: any[] = []; for (let [name, plugin] of plugins) { try { - // Use an object to detect if the hook failed on timeout or tried to return a value looking like an error - const timeout = {}; - let result = await libHelpers.timeout( - (plugin as any)[hook](...args), + const timeout = Symbol("timeout-token"); + let result = await libHelpers.timeout( + plugin[hook](...args) as Promise, 15000, timeout );