# 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 `EnginePlugin` so the `PluginManager` can install/enable/disable any subclass without per-plugin glue. - Template-method hooks (`onInstallHook`, `onUninstallHook`, `onEnableHook`, `onDisableHook`) — subclasses override only what they need. - Injectable `Clock` for deterministic timestamping; defaults to `Date.now`. - Injectable `LoggerLike` (defaults to no-op until installed; install promotes the manager-supplied logger). - Public `state` getter and `snapshot()` accessor for observability. - `assertLive(op)` helper subclasses call from public methods to fail fast on use-after-destroy with `GAME_SERVER.PLUGIN_BASE_DESTROYED`. ## Usage: Basic ```ts 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 { this.logger.info("hello plugin installed"); } } ``` ## Usage: Advanced ```ts 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 { this.installs += 1; } public count(): number { this.assertLive("count"); return this.installs; } } ``` ## API Reference - `class BasePlugin implements EnginePlugin` - `abstract readonly name: string` - `abstract readonly version: string` - `readonly dependencies?: readonly string[]` - `get state(): BasePluginState` - `snapshot(): BasePluginSnapshot` - `onInstall(ctx): Promise` - `onUninstall(ctx): Promise` - `onEnable(ctx): Promise` - `onDisable(ctx): Promise` - `protected onInstallHook(ctx): Promise` (override) - `protected onUninstallHook(ctx): Promise` (override) - `protected onEnableHook(ctx): Promise` (override) - `protected onDisableHook(ctx): Promise` (override) - `protected assertLive(op: string): void` ## Implementation Notes - The state machine is deliberately minimal: every hook except `onUninstall` throws `PLUGIN_BASE_DESTROYED` when the plugin has already been torn down. `onUninstall` itself is also rejected after destroy because the `PluginManager` removes 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 `onInstall` runs, the context logger from `PluginManager` is adopted unless the caller supplied an explicit override via `BasePluginOptions`. - The clock is captured at construction time so callers can substitute a `FakeClock` from `@suaveplan/testing` for deterministic snapshots.