/** * Plugin interfaces and utilities. * @module lib/plugin */ import * as libHelpers from "./helpers"; import type { Logger } from "./logging"; import type { FieldDefinition } from "./config"; import type { PlayerStats } from "./data"; export const PluginFeatureFlags = [ /** The plugin requires module code to be patched into the save */ "SavePatching", /** The plugin requires access to script commands over rcon */ "ScriptCommands", ] as const; /** Used to define the plugin export in plugins */ export type PluginDeclaration = { name: string; title: string; description?: string; controllerEntrypoint?: string; controllerConfigFields?: Record; hostEntrypoint?: string; hostConfigFields?: Record; instanceEntrypoint?: string; instanceConfigFields?: Record; ctlEntrypoint?: string; webEntrypoint?: string; controlConfigFields?: Record; features?: (typeof PluginFeatureFlags)[number][]; messages?: any[]; routes?: string[]; } export type PluginNodeEnvInfo = PluginDeclaration & { requirePath: string; version: string; manifest: any; /** * NPM package this plugin is published as. Not present if the package * is private or the path used to load it does not match the name of the * package. */ npmPackage?: string; }; export type PluginWebpackEnvInfo = PluginDeclaration & { container?: any; package?: any; enabled?: boolean; error?: string; }; /** * Information about the event. */ export interface PlayerEvent { type: "join" | "leave" | "import" | "promote" | "demote" | "ban" | "unban" | "whitelisted" | "unwhitelisted"; /** Name of the player that caused the event */ name: string, /** * Only present for type "leave" and "ban". Reason for player leaving the * game, one of the possible reasons in defines.disconnect_reason * or "server_quit" if the server exits while the player is online. * When type is "ban" this is the reason given for the ban. */ reason?: string; /** * Statistics recorded for this player on the instance this event * originated from. */ stats: PlayerStats, } /** * Invokes the given hook on all plugins * * @param plugins - * Mapping of plugin names to plugins to invoke the hook on. * @param hook - Name of hook to invoke. * @param args - Arguments to pass on to the hook. * @returns Non-undefined return values from the hooks. */ 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 { const timeout = Symbol("timeout-token"); let result = await libHelpers.timeout( plugin[hook](...args) as Promise, 15000, timeout ); if (result === timeout) { throw new Error(`Invoking hook ${hook} timed out for plugin ${name}`); } else if (result !== undefined) { results.push(result); } } catch (err: any) { plugin.logger.error(`Ignoring error from plugin ${name} in ${hook}:\n${err.stack}`); } } return results; }