3.7 KiB
3.7 KiB
BasePlugin
(no submodules)
Purpose
BasePlugin is the abstract base class every concrete @suaveplan/game-server
plugin extends. It implements the EnginePlugin contract that the
PluginManager consumes (onInstall, onUninstall, onEnable, onDisable)
and forwards each call to a protected template-method hook
(onInstallHook etc.) that subclasses override.
The base also owns a small lifecycle state machine
(created → installed → enabled → disabled → destroyed) and refuses to run
any hook once the plugin has reached destroyed. This catches the common
"resurrect a torn-down plugin" bug at the boundary instead of letting it
corrupt downstream state.
Features
- Implements
EnginePluginso thePluginManagercan install/enable/disable any subclass without per-plugin glue. - Template-method hooks (
onInstallHook,onUninstallHook,onEnableHook,onDisableHook) — subclasses override only what they need. - Injectable
Clockfor deterministic timestamping; defaults toDate.now. - Injectable
LoggerLike(defaults to no-op until installed; install promotes the manager-supplied logger). - Public
stategetter andsnapshot()accessor for observability. assertLive(op)helper subclasses call from public methods to fail fast on use-after-destroy withGAME_SERVER.PLUGIN_BASE_DESTROYED.
Usage: Basic
import { BasePlugin } from "@suaveplan/game-server/plugins/base-plugin";
class HelloPlugin extends BasePlugin {
public readonly name = "hello";
public readonly version = "1.0.0";
protected async onInstallHook(): Promise<void> {
this.logger.info("hello plugin installed");
}
}
Usage: Advanced
import { BasePlugin } from "@suaveplan/game-server/plugins/base-plugin";
import type { EnginePluginContext } from "@suaveplan/game-server/plugins/base-plugin";
class CountingPlugin extends BasePlugin {
public readonly name = "counter";
public readonly version = "1.0.0";
public readonly dependencies = ["hello"] as const;
private installs = 0;
public constructor() {
super({ clock: () => 1_700_000_000_000 });
}
protected async onInstallHook(_ctx: EnginePluginContext): Promise<void> {
this.installs += 1;
}
public count(): number {
this.assertLive("count");
return this.installs;
}
}
API Reference
class BasePlugin implements EnginePluginabstract readonly name: stringabstract readonly version: stringreadonly dependencies?: readonly string[]get state(): BasePluginStatesnapshot(): BasePluginSnapshotonInstall(ctx): Promise<void>onUninstall(ctx): Promise<void>onEnable(ctx): Promise<void>onDisable(ctx): Promise<void>protected onInstallHook(ctx): Promise<void>(override)protected onUninstallHook(ctx): Promise<void>(override)protected onEnableHook(ctx): Promise<void>(override)protected onDisableHook(ctx): Promise<void>(override)protected assertLive(op: string): void
Implementation Notes
- The state machine is deliberately minimal: every hook except
onUninstallthrowsPLUGIN_BASE_DESTROYEDwhen the plugin has already been torn down.onUninstallitself is also rejected after destroy because thePluginManagerremoves the entry from its map on the first uninstall. - The logger field starts as a no-op so subclass constructors can log
defensively without crashing tests that omit a logger; once
onInstallruns, the context logger fromPluginManageris adopted unless the caller supplied an explicit override viaBasePluginOptions. - The clock is captured at construction time so callers can substitute a
FakeClockfrom@suaveplan/testingfor deterministic snapshots.