mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 07:24:52 +09:00
Add typed hook system for plugins
Replace invokeHook and the per plugin on* methods with AsyncHook collections on the controller, host, instance, clusterioctl and web control objects. Plugin entrypoints export a default function that receives a load context and attaches handlers to the hooks it needs. Class exports still load through fromContext with a deprecation warning. The create templates and docs use the entrypoint function. This is clusterio/clusterio#921 recreated on top of master after the ESM migration and the PluginDeclaration permissions change. Closes #730 Co-Authored-By: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c03ac15ab
commit
7122cbccf8
64 files changed
+1878
-1184
No files matched your search
@@ -1,45 +1,35 @@
|
||||
import type {
|
||||
CollectorResult, Event, InstanceStatus, Logger,
|
||||
ModPack, ModInfo, PlayerEvent, PluginNodeEnvInfo,
|
||||
} from "@clusterio/lib";
|
||||
import type Controller from "./Controller.js";
|
||||
import type InstanceRecord from "./InstanceRecord.js";
|
||||
import type ControlConnection from "./ControlConnection.js";
|
||||
import type HostConnection from "./HostConnection.js";
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
export type ControllerPluginContext = lib.PluginLoadContext<{
|
||||
controller: Controller;
|
||||
metrics: any;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Base class for controller plugins
|
||||
*
|
||||
* Controller plugins are subclasses of this class which get instantiated by
|
||||
* the controller on startup when the plugin is enabled in the config.
|
||||
* To be discovered the class must be exported under the name `ControllerPlugin`
|
||||
* in the module specified by the `controllerEntrypoint` in the plugin's
|
||||
* `plugin` export.
|
||||
* Collection of controller plugin hooks
|
||||
*/
|
||||
export default class BaseControllerPlugin {
|
||||
/**
|
||||
* Logger for this plugin
|
||||
*
|
||||
* Instance of winston Logger for sending log messages from this
|
||||
* plugin. Supported methods and their corresponding log levels are
|
||||
* `error`, `warn`, `audit`, `info` and `verbose`.
|
||||
*/
|
||||
logger: Logger;
|
||||
|
||||
constructor(
|
||||
public info: PluginNodeEnvInfo,
|
||||
public controller: Controller,
|
||||
public metrics: any,
|
||||
logger: Logger
|
||||
) {
|
||||
this.logger = logger.child({ plugin: this.info.name }) as unknown as Logger;
|
||||
export class ControllerHooks extends lib.AsyncHookCollection {
|
||||
constructor(logger: lib.Logger) {
|
||||
super(logger);
|
||||
this.save = this.newHook();
|
||||
this.metrics = this.newHook();
|
||||
this.shutdown = this.newHook();
|
||||
this.instanceStatusChanged = this.newHook();
|
||||
this.instanceConfigFieldChanged = this.newHook();
|
||||
this.controllerConfigFieldChanged = this.newHook();
|
||||
this.controlConnectionEvent = this.newHook();
|
||||
this.hostConnectionEvent = this.newHook();
|
||||
this.prepareHostDisconnect = this.newHook();
|
||||
this.modPacksUpdated = this.newHook();
|
||||
this.modsUpdated = this.newHook();
|
||||
this.rolesUpdated = this.newHook();
|
||||
this.playerEvent = this.newHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after the class is instantiated
|
||||
*/
|
||||
async init() { }
|
||||
|
||||
/**
|
||||
* Called when the controller saves data in memory to disk
|
||||
*
|
||||
@@ -51,7 +41,7 @@ export default class BaseControllerPlugin {
|
||||
* BaseControllerPlugin.onShutdown} have been invoked and all links have
|
||||
* been disconnected.
|
||||
*/
|
||||
async onSaveData() { }
|
||||
readonly save: lib.AsyncHook<[]>;
|
||||
|
||||
/**
|
||||
* Called when the status of an instance changes
|
||||
@@ -91,7 +81,7 @@ export default class BaseControllerPlugin {
|
||||
* The instance that changed.
|
||||
* @param prev - the previous status of the instance.
|
||||
*/
|
||||
async onInstanceStatusChanged(instance: InstanceRecord, prev?: InstanceStatus) { }
|
||||
readonly instanceStatusChanged: lib.AsyncHook<[instance: InstanceRecord, prev: lib.InstanceStatus | undefined]>;
|
||||
|
||||
/**
|
||||
* Called when the value of a controller config field changed.
|
||||
@@ -103,7 +93,7 @@ export default class BaseControllerPlugin {
|
||||
* @param curr - The current value of the field.
|
||||
* @param prev - The previous value of the field.
|
||||
*/
|
||||
async onControllerConfigFieldChanged(field: string, curr: unknown, prev: unknown) { }
|
||||
readonly controllerConfigFieldChanged: lib.AsyncHook<[field: string, curr: unknown, prev: unknown]>;
|
||||
|
||||
/**
|
||||
* Called when the value of an instance config field changed.
|
||||
@@ -117,7 +107,8 @@ export default class BaseControllerPlugin {
|
||||
* @param curr - The current value of the field.
|
||||
* @param prev - The previous value of the field.
|
||||
*/
|
||||
async onInstanceConfigFieldChanged(instance: InstanceRecord, field: string, curr: unknown, prev: unknown) { }
|
||||
// eslint-disable-next-line max-len
|
||||
readonly instanceConfigFieldChanged: lib.AsyncHook<[instance: InstanceRecord, field: string, curr: unknown, prev: unknown]>;
|
||||
|
||||
/**
|
||||
* Called before collecting Prometheus metrics
|
||||
@@ -134,12 +125,12 @@ export default class BaseControllerPlugin {
|
||||
*
|
||||
* @returns an async iterator of prometheus metric results or undefined.
|
||||
*/
|
||||
async onMetrics(): Promise<void | AsyncIterable<CollectorResult>> { }
|
||||
readonly metrics: lib.AsyncHook<[], AsyncIterable<lib.CollectorResult>>;
|
||||
|
||||
/**
|
||||
* Called when the controller is shutting down
|
||||
*/
|
||||
async onShutdown() { }
|
||||
readonly shutdown: lib.AsyncHook<[]>;
|
||||
|
||||
/**
|
||||
* Called when an event on a host connection happens
|
||||
@@ -174,7 +165,8 @@ export default class BaseControllerPlugin {
|
||||
* The connection the event occured on.
|
||||
* @param event - one of connect, drop, resume and close
|
||||
*/
|
||||
onHostConnectionEvent(connection: HostConnection, event: "connect" | "drop" | "resume" | "close") { }
|
||||
// eslint-disable-next-line max-len
|
||||
readonly hostConnectionEvent: lib.AsyncHook<[connection: HostConnection, event: "connect" | "drop" | "resume" | "close"]>;
|
||||
|
||||
/**
|
||||
* Called when an avent on a control connection happens
|
||||
@@ -210,7 +202,8 @@ export default class BaseControllerPlugin {
|
||||
* The connection the event occured on.
|
||||
* @param event - one of connect, drop, resume, and close.
|
||||
*/
|
||||
onControlConnectionEvent(connection: ControlConnection, event: "connect" | "drop" | "resume" | "close") { }
|
||||
// eslint-disable-next-line max-len
|
||||
readonly controlConnectionEvent: lib.AsyncHook<[connection: ControlConnection, event: "connect" | "drop" | "resume" | "close"]>;
|
||||
|
||||
/**
|
||||
* Called when a host is preparing to disconnect from the controller
|
||||
@@ -224,7 +217,7 @@ export default class BaseControllerPlugin {
|
||||
* @param connection -
|
||||
* The connection to the host preparing to disconnect.
|
||||
*/
|
||||
async onPrepareHostDisconnect(connection: HostConnection) { }
|
||||
readonly prepareHostDisconnect: lib.AsyncHook<[connection: HostConnection]>;
|
||||
|
||||
/**
|
||||
* Called when one or more mod packs are updated
|
||||
@@ -237,7 +230,7 @@ export default class BaseControllerPlugin {
|
||||
*
|
||||
* @param modPacks - Mod packs that updated.
|
||||
*/
|
||||
async onModPacksUpdated(modPacks: ModPack[]) { }
|
||||
readonly modPacksUpdated: lib.AsyncHook<[modPacks: lib.ModPack[]]>;
|
||||
|
||||
/**
|
||||
* Called when one or more mod stored on the controller are updated
|
||||
@@ -249,7 +242,18 @@ export default class BaseControllerPlugin {
|
||||
*
|
||||
* @param mods - Mods that updated.
|
||||
*/
|
||||
async onModsUpdated(mods: ModInfo[]) { }
|
||||
readonly modsUpdated: lib.AsyncHook<[mods: lib.ModInfo[]]>;
|
||||
|
||||
/**
|
||||
* Called when one or more roles stored on the controller are updated
|
||||
*
|
||||
* Invoked when one or more roles have been added, updated or deleted.
|
||||
*
|
||||
* If a role has been deleted its `.isDeleted` property will be true.
|
||||
*
|
||||
* @param roles - Roles that updated.
|
||||
*/
|
||||
readonly rolesUpdated: lib.AsyncHook<[roles: lib.Role[]]>;
|
||||
|
||||
/**
|
||||
* Called when a player joins or leaves an instance
|
||||
@@ -261,7 +265,79 @@ export default class BaseControllerPlugin {
|
||||
* The instance it occured on.
|
||||
* @param event - Information about the event.
|
||||
*/
|
||||
async onPlayerEvent(instance: InstanceRecord, event: PlayerEvent) { }
|
||||
readonly playerEvent: lib.AsyncHook<[instance: InstanceRecord, event: lib.PlayerEvent]>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
export interface BaseControllerPlugin {
|
||||
onSaveData?(): Promise<void>;
|
||||
onMetrics?(): Promise<void | AsyncIterable<lib.CollectorResult>>;
|
||||
onShutdown?(): Promise<void>;
|
||||
onInstanceStatusChanged?(instance: InstanceRecord, prev?: lib.InstanceStatus): Promise<void>;
|
||||
onControllerConfigFieldChanged?(field: string, curr: unknown, prev: unknown): Promise<void>;
|
||||
onInstanceConfigFieldChanged?(instance: InstanceRecord, field: string, curr: unknown, prev: unknown): Promise<void>;
|
||||
onHostConnectionEvent?(connection: HostConnection, event: "connect" | "drop" | "resume" | "close"): void;
|
||||
onControlConnectionEvent?(connection: ControlConnection, event: "connect" | "drop" | "resume" | "close"): void;
|
||||
onPrepareHostDisconnect?(connection: HostConnection): Promise<void>;
|
||||
onModPacksUpdated?(modPacks: lib.ModPack[]): Promise<void>;
|
||||
onModsUpdated?(mods: lib.ModInfo[]): Promise<void>;
|
||||
onRolesUpdated?(roles: lib.Role[]): Promise<void>;
|
||||
onPlayerEvent?(instance: InstanceRecord, event: lib.PlayerEvent): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for controller plugins
|
||||
*
|
||||
* Controller plugins are subclasses of this class which get instantiated by
|
||||
* the controller on startup when the plugin is enabled in the config.
|
||||
* To be discovered the class must be exported under the name `ControllerPlugin`
|
||||
* in the module specified by the `controllerEntrypoint` in the plugin's
|
||||
* `plugin` export.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||
export class BaseControllerPlugin {
|
||||
constructor(
|
||||
public info: lib.PluginNodeEnvInfo,
|
||||
public controller: Controller,
|
||||
public metrics: any,
|
||||
public logger: lib.Logger,
|
||||
) {
|
||||
const attach = <Args extends unknown[], Return>(
|
||||
hook: lib.AsyncHook<Args, Return>,
|
||||
fn?: lib.HookHandler<Args, Return>,
|
||||
) => {
|
||||
if (fn) {
|
||||
hook.attach(info.name, fn.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
attach(controller.hooks.save, this.onSaveData);
|
||||
attach(controller.hooks.instanceStatusChanged, this.onInstanceStatusChanged);
|
||||
attach(controller.hooks.controllerConfigFieldChanged, this.onControllerConfigFieldChanged);
|
||||
attach(controller.hooks.instanceConfigFieldChanged, this.onInstanceConfigFieldChanged);
|
||||
attach(controller.hooks.metrics, this.onMetrics);
|
||||
attach(controller.hooks.shutdown, this.onShutdown);
|
||||
attach(controller.hooks.hostConnectionEvent, this.onHostConnectionEvent);
|
||||
attach(controller.hooks.controlConnectionEvent, this.onControlConnectionEvent);
|
||||
attach(controller.hooks.prepareHostDisconnect, this.onPrepareHostDisconnect);
|
||||
attach(controller.hooks.modPacksUpdated, this.onModPacksUpdated);
|
||||
attach(controller.hooks.modsUpdated, this.onModsUpdated);
|
||||
attach(controller.hooks.rolesUpdated, this.onRolesUpdated);
|
||||
attach(controller.hooks.playerEvent, this.onPlayerEvent);
|
||||
}
|
||||
|
||||
static fromContext(context: ControllerPluginContext) {
|
||||
return new this(context.plugin, context.controller, context.metrics, context.logger);
|
||||
}
|
||||
|
||||
detachHooks() {
|
||||
this.controller.hooks.detachAll(this.info.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after the class is instantiated
|
||||
*/
|
||||
async init() {}
|
||||
|
||||
/**
|
||||
* Broadcast event to all connected hosts
|
||||
@@ -272,7 +348,7 @@ export default class BaseControllerPlugin {
|
||||
*
|
||||
* @param event - Event to send
|
||||
*/
|
||||
broadcastEventToHosts<T>(event: Event<T>) {
|
||||
broadcastEventToHosts<T>(event: lib.Event<T>) {
|
||||
for (let hostConnection of this.controller.wsServer.hostConnections.values()) {
|
||||
if (
|
||||
!hostConnection.connector.closing
|
||||
|
||||
@@ -63,9 +63,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
|
||||
for (let event of ["connect", "drop", "resume", "close"] as const) {
|
||||
this.connector.on(event, () => {
|
||||
for (let controllerPlugin of this._controller.plugins.values()) {
|
||||
controllerPlugin.onControlConnectionEvent(this, event);
|
||||
}
|
||||
this._controller.hooks.controlConnectionEvent.invoke(this, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1310,7 +1308,7 @@ export default class ControlConnection extends BaseConnection {
|
||||
async handlePluginListRequest(request: lib.PluginListRequest) {
|
||||
return this._controller.pluginInfos.map(pluginInfo => lib.PluginDetails.fromNodeEnvInfo(
|
||||
pluginInfo,
|
||||
this._controller.plugins.has(pluginInfo.name),
|
||||
this._controller.loadedPlugins.has(pluginInfo),
|
||||
this._controller.config.get(`${pluginInfo.name}.load_plugin`),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import UserRecord from "./UserRecord.js";
|
||||
import UserManager from "./UserManager.js";
|
||||
import WsServer from "./WsServer.js";
|
||||
import HostRecord from "./HostRecord.js";
|
||||
import BaseControllerPlugin from "./BaseControllerPlugin.js";
|
||||
import { BaseControllerPlugin, ControllerHooks } from "./BaseControllerPlugin.js";
|
||||
import ControllerRouter from "./ControllerRouter.js";
|
||||
import InstanceManager from "./InstanceManager.js";
|
||||
|
||||
@@ -70,8 +70,10 @@ export default class Controller {
|
||||
httpsServer: https.Server | null = null;
|
||||
httpsServerCloser: HttpCloser | null = null;
|
||||
|
||||
/** Mapping of plugin name to loaded plugin */
|
||||
plugins: Map<string, BaseControllerPlugin> = new Map();
|
||||
/** Hooks which plugins can attach to */
|
||||
hooks = new ControllerHooks(logger);
|
||||
/** Plugins which are currently loaded */
|
||||
loadedPlugins: Set<lib.PluginNodeEnvInfo> = new Set();
|
||||
|
||||
/** WebSocket server */
|
||||
wsServer: WsServer;
|
||||
@@ -359,7 +361,7 @@ export default class Controller {
|
||||
} else if (field === "controller.static_url") {
|
||||
this.app.locals.staticRoot = this.config.get("controller.static_url");
|
||||
}
|
||||
lib.invokeHook(this.plugins, "onControllerConfigFieldChanged", field, curr, prev);
|
||||
this.hooks.controllerConfigFieldChanged.invoke(field, curr, prev);
|
||||
});
|
||||
|
||||
// Make sure we're actually going to listen on a port
|
||||
@@ -542,7 +544,7 @@ export default class Controller {
|
||||
await new Promise((resolve, reject) => { this.devMiddleware.close(resolve); });
|
||||
}
|
||||
|
||||
await lib.invokeHook(this.plugins, "onShutdown");
|
||||
await this.hooks.shutdown.invoke();
|
||||
|
||||
await this.wsServer.stop();
|
||||
|
||||
@@ -732,9 +734,8 @@ export default class Controller {
|
||||
this.modPacks.save(),
|
||||
this.roles.save(),
|
||||
this.users.records.save(),
|
||||
this.hooks.save.invoke(),
|
||||
]);
|
||||
|
||||
await lib.invokeHook(this.plugins, "onSaveData");
|
||||
}
|
||||
|
||||
static migrateSystems(rawJson: unknown[]): Static<typeof lib.SystemInfo.jsonSchema>[] {
|
||||
@@ -1034,7 +1035,7 @@ export default class Controller {
|
||||
|
||||
modPacksUpdated(modPacks: lib.ModPack[]) {
|
||||
this.subscriptions.broadcast(new lib.ModPackUpdatesEvent(modPacks));
|
||||
lib.invokeHook(this.plugins, "onModPacksUpdated", modPacks);
|
||||
this.hooks.modPacksUpdated.invoke(modPacks);
|
||||
}
|
||||
|
||||
async handleModPackSubscription(request: lib.SubscriptionRequest) {
|
||||
@@ -1050,7 +1051,7 @@ export default class Controller {
|
||||
modsUpdated(mods: lib.ModInfo[]) {
|
||||
// ModStore sets updatedAtMs for mods
|
||||
this.subscriptions.broadcast(new lib.ModUpdatesEvent(mods));
|
||||
lib.invokeHook(this.plugins, "onModsUpdated", mods);
|
||||
this.hooks.modsUpdated.invoke(mods);
|
||||
}
|
||||
|
||||
async handleModSubscription(request: lib.SubscriptionRequest) {
|
||||
@@ -1098,7 +1099,7 @@ export default class Controller {
|
||||
|
||||
rolesUpdated(roles: lib.Role[]) {
|
||||
this.subscriptions.broadcast(new lib.RoleUpdatesEvent(roles));
|
||||
// lib.invokeHook(this.plugins, "onRolesUpdated", roles); // This doesn't exist at the moment
|
||||
this.hooks.rolesUpdated.invoke(roles);
|
||||
// Notify connected control clients with the given role that the permissions may have changed.
|
||||
for (const role of roles) {
|
||||
for (let controlConnection of this.wsServer.controlConnections.values()) {
|
||||
@@ -1128,6 +1129,7 @@ export default class Controller {
|
||||
}
|
||||
|
||||
async loadPlugins() {
|
||||
const context = { metrics, logger, controller: this };
|
||||
for (let pluginInfo of this.pluginInfos) {
|
||||
try {
|
||||
let manifestPath = path.posix.join(pluginInfo.requirePath, "dist", "web", "manifest.json");
|
||||
@@ -1145,20 +1147,16 @@ export default class Controller {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ControllerPluginClass = BaseControllerPlugin;
|
||||
try {
|
||||
if (pluginInfo.controllerEntrypoint) {
|
||||
ControllerPluginClass = await lib.loadPluginClass(
|
||||
pluginInfo.name,
|
||||
path.posix.join(pluginInfo.requirePath, pluginInfo.controllerEntrypoint),
|
||||
"ControllerPlugin",
|
||||
BaseControllerPlugin,
|
||||
);
|
||||
}
|
||||
await lib.loadPlugin(
|
||||
pluginInfo,
|
||||
"controller",
|
||||
context,
|
||||
"ControllerPlugin",
|
||||
BaseControllerPlugin,
|
||||
);
|
||||
|
||||
let controllerPlugin = new ControllerPluginClass(pluginInfo, this, metrics as any, logger);
|
||||
await controllerPlugin.init();
|
||||
this.plugins.set(pluginInfo.name, controllerPlugin);
|
||||
this.loadedPlugins.add(pluginInfo);
|
||||
|
||||
} catch (err: any) {
|
||||
if (err.code === "InstallationError") {
|
||||
|
||||
@@ -47,12 +47,7 @@ export default class HostConnection extends BaseConnection {
|
||||
this._checkPluginVersions();
|
||||
|
||||
for (let event of ["connect", "drop", "resume", "close"] as const) {
|
||||
|
||||
this.connector.on(event, () => {
|
||||
for (let plugin of this._controller.plugins.values()) {
|
||||
plugin.onHostConnectionEvent(this, event);
|
||||
}
|
||||
});
|
||||
this.connector.on(event, () => this._controller.hooks.hostConnectionEvent.invoke(this, event));
|
||||
}
|
||||
|
||||
this.connector.on("close", () => {
|
||||
@@ -66,7 +61,7 @@ export default class HostConnection extends BaseConnection {
|
||||
instances.push(instance);
|
||||
let prev = instance.status;
|
||||
instance.status = "unknown";
|
||||
lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", instance, prev);
|
||||
this._controller.hooks.instanceStatusChanged.invoke(instance, prev);
|
||||
}
|
||||
this._controller.instances.records.setMany(instances);
|
||||
|
||||
@@ -143,7 +138,7 @@ export default class HostConnection extends BaseConnection {
|
||||
}
|
||||
|
||||
async prepareDisconnect() {
|
||||
await lib.invokeHook(this._controller.plugins, "onPrepareHostDisconnect", this);
|
||||
await this._controller.hooks.prepareHostDisconnect.invoke(this);
|
||||
return await super.prepareDisconnect();
|
||||
}
|
||||
|
||||
@@ -216,7 +211,7 @@ export default class HostConnection extends BaseConnection {
|
||||
instance.factorioVersion = request.factorioVersion;
|
||||
this._controller.instances.records.set(instance);
|
||||
logger.verbose(`Instance ${instance.config.get("instance.name")} State: ${instance.status}`);
|
||||
await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", instance, prev);
|
||||
await this._controller.hooks.instanceStatusChanged.invoke(instance, prev);
|
||||
}
|
||||
|
||||
async handleInstancesUpdateRequest(request: lib.InstancesUpdateRequest) {
|
||||
@@ -256,9 +251,7 @@ export default class HostConnection extends BaseConnection {
|
||||
}
|
||||
instanceUpdates.push(controllerInstance);
|
||||
logger.verbose(`Instance ${instanceConfig.get("instance.name")} State: ${instanceData.status}`);
|
||||
await lib.invokeHook(
|
||||
this._controller.plugins, "onInstanceStatusChanged", controllerInstance, prev
|
||||
);
|
||||
await this._controller.hooks.instanceStatusChanged.invoke(controllerInstance, prev);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -274,7 +267,7 @@ export default class HostConnection extends BaseConnection {
|
||||
// We suppress the changes made by createInstance so we must manually make them here
|
||||
this._controller.instances.records.setMany(instanceUpdates);
|
||||
await Promise.all(instanceUpdates.flatMap(newInstance => [
|
||||
lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", newInstance),
|
||||
this._controller.hooks.instanceStatusChanged.invoke(newInstance, undefined),
|
||||
this.send(
|
||||
new lib.InstanceAssignInternalRequest(
|
||||
newInstance.config.get("instance.id"), newInstance.config.toRemote("host")
|
||||
@@ -350,7 +343,7 @@ export default class HostConnection extends BaseConnection {
|
||||
user.saveRecord();
|
||||
|
||||
let instance = this._controller.instances.get(instanceId)!;
|
||||
await lib.invokeHook(this._controller.plugins, "onPlayerEvent", instance, {
|
||||
await this._controller.hooks.playerEvent.invoke(instance, {
|
||||
type: event.type,
|
||||
name: event.name,
|
||||
reason: event.reason,
|
||||
@@ -367,7 +360,7 @@ export default class HostConnection extends BaseConnection {
|
||||
user.set("isAdmin", event.admin);
|
||||
|
||||
const instance = this._controller.instances.get(src.id)!;
|
||||
await lib.invokeHook(this._controller.plugins, "onPlayerEvent", instance, {
|
||||
await this._controller.hooks.playerEvent.invoke(instance, {
|
||||
type: event.admin ? "promote" : "demote" as lib.PlayerEvent["type"],
|
||||
name: event.name,
|
||||
stats: user.instanceStats.get(src.id)!,
|
||||
@@ -385,11 +378,11 @@ export default class HostConnection extends BaseConnection {
|
||||
user.saveRecord();
|
||||
|
||||
const instance = this._controller.instances.get(src.id)!;
|
||||
await lib.invokeHook(this._controller.plugins, "onPlayerEvent", instance, {
|
||||
await this._controller.hooks.playerEvent.invoke(instance, {
|
||||
type: event.banned ? "ban" : "unban" as lib.PlayerEvent["type"],
|
||||
name: event.name,
|
||||
stats: user.instanceStats.get(src.id)!,
|
||||
resaon: event.reason,
|
||||
reason: event.reason,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -402,7 +395,7 @@ export default class HostConnection extends BaseConnection {
|
||||
user.set("isWhitelisted", event.whitelisted);
|
||||
|
||||
const instance = this._controller.instances.get(src.id)!;
|
||||
await lib.invokeHook(this._controller.plugins, "onPlayerEvent", instance, {
|
||||
await this._controller.hooks.playerEvent.invoke(instance, {
|
||||
type: event.whitelisted ? "whitelisted" : "unwhitelisted" as lib.PlayerEvent["type"],
|
||||
name: event.name,
|
||||
stats: user.instanceStats.get(src.id)!,
|
||||
|
||||
@@ -83,12 +83,7 @@ export default class InstanceManager {
|
||||
instance: InstanceRecord,
|
||||
prev?: lib.InstanceStatus,
|
||||
): Promise<void> {
|
||||
await lib.invokeHook(
|
||||
this._controller.plugins,
|
||||
"onInstanceStatusChanged",
|
||||
instance,
|
||||
prev,
|
||||
);
|
||||
await this._controller.hooks.instanceStatusChanged.invoke(instance, prev);
|
||||
}
|
||||
|
||||
private async _notifyConfigFieldChanged(
|
||||
@@ -97,14 +92,7 @@ export default class InstanceManager {
|
||||
curr: unknown,
|
||||
prev: unknown,
|
||||
): Promise<void> {
|
||||
await lib.invokeHook(
|
||||
this._controller.plugins,
|
||||
"onInstanceConfigFieldChanged",
|
||||
instance,
|
||||
field,
|
||||
curr,
|
||||
prev,
|
||||
);
|
||||
await this._controller.hooks.instanceConfigFieldChanged.invoke(instance, field, curr, prev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -135,8 +135,8 @@ export default class WsServer {
|
||||
|
||||
// Start connection handshake.
|
||||
let loadedPlugins: {[key:string]: string} = {};
|
||||
for (let [name, plugin] of this.controller.plugins) {
|
||||
loadedPlugins[name] = plugin.info.version;
|
||||
for (const plugin of this.controller.loadedPlugins) {
|
||||
loadedPlugins[plugin.name] = plugin.version;
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify(new lib.MessageHello(new lib.HelloData(packageConfig.version, loadedPlugins))));
|
||||
|
||||
@@ -49,7 +49,7 @@ async function getMetrics(req: Request, res: Response, next: any) {
|
||||
const controller: Controller = req.app.locals.controller;
|
||||
|
||||
let results: lib.CollectorResult[] = [];
|
||||
let pluginResults = await lib.invokeHook(controller.plugins, "onMetrics");
|
||||
let pluginResults = await controller.hooks.metrics.collect();
|
||||
for (let metricIterator of pluginResults) {
|
||||
for await (let metric of metricIterator) {
|
||||
results.push(metric);
|
||||
@@ -117,8 +117,8 @@ function getPlugins(req: Request, res: Response) {
|
||||
let plugins: lib.PluginWebApi[] = [];
|
||||
for (let pluginInfo of req.app.locals.controller.pluginInfos) {
|
||||
let name = pluginInfo.name;
|
||||
let loaded = req.app.locals.controller.plugins.has(name);
|
||||
let enabled = loaded && req.app.locals.controller.config.get(`${pluginInfo.name}.load_plugin`);
|
||||
const loaded = req.app.locals.controller.loadedPlugins.has(pluginInfo);
|
||||
const enabled = loaded && req.app.locals.controller.config.get(`${pluginInfo.name}.load_plugin`);
|
||||
// Note: Cast through unknown is needed because load_plugin is
|
||||
// defined at runtime and no other fields in the controller config
|
||||
// currently have the boolean type.
|
||||
|
||||
Reference in new issue
Block a user