first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# RulesPlugin
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`RulesPlugin` integrates `@suaveplan/rules` with the game-server engine. It
maintains three rule sets — move validation, game start, and game end — and
exposes `evaluateMove`, `evaluateGameStart`, and `evaluateGameEnd` so the
engine can apply declarative checks without leaking the underlying engine
abstraction into game logic.
The plugin's role is integration glue: it does not interpret rules itself,
it just hands them to the injected `RuleEngine` and records the pass/fail
outcomes in a rolling history. Callers compose rule sets at construction
time or replace them at runtime via `setMoveRules` / `addMoveRules`.
## Features
- Three named rule slots (move / start / end) plus an ad-hoc `evaluate`
helper.
- Empty rule sets are treated as a "passed" no-op (recorded in history).
- Rolling evaluation history with a configurable cap (default 1 000).
- Mutable move rule set: append (`addMoveRules`) or replace
(`setMoveRules`) at runtime.
- Zod-validated `historyCap` option.
- Lifecycle-safe: clears rule sets and history on uninstall.
## Usage: Basic
```ts
import { RuleEngine } from "@suaveplan/rules";
import { RulesPlugin } from "@suaveplan/game-server/plugins/rules-plugin";
const engine = new RuleEngine();
const plugin = new RulesPlugin({ engine });
await plugin.evaluateMove("p1", { kind: "attack" }, { hp: 10 });
```
## Usage: Advanced
```ts
import { RuleEngine, type Rule } from "@suaveplan/rules";
import { RulesPlugin } from "@suaveplan/game-server/plugins/rules-plugin";
const engine = new RuleEngine();
const damageCap: Rule = /* ... discriminated rule ... */ ({} as Rule);
const plugin = new RulesPlugin({
engine,
moveValidationRules: [damageCap],
historyCap: 50,
clock: () => 1_700_000_000_000,
});
plugin.addMoveRules(/* another rule */);
const results = await plugin.evaluateMove("p1", { dmg: 5 }, { hp: 10 });
const history = plugin.getHistory();
```
## API Reference
- `class RulesPlugin extends BasePlugin`
- `constructor(options: RulesPluginOptions)`
- `evaluateMove(playerId, move, gameState): Promise<readonly EvaluationResult[]>`
- `evaluateGameStart(sessionId): Promise<readonly EvaluationResult[]>`
- `evaluateGameEnd(sessionId): Promise<readonly EvaluationResult[]>`
- `evaluate(rules, context): Promise<readonly EvaluationResult[]>`
- `addMoveRules(...rules): void`
- `setMoveRules(rules): void`
- `getHistory(): readonly EvaluationHistoryEntry[]`
- `clearHistory(): void`
## Implementation Notes
- The engine API used here is `evaluateAll(rules, context)`, which
returns an array of `EvaluationResult` (one per rule). The plugin
records a single history entry per evaluation, with `passed` reflecting
the conjunction over the result set.
- `evaluate(rules, ctx)` rejects non-object contexts with
`GAME_SERVER.INVALID_RULE_CONTEXT` so callers cannot silently pass
primitives through to the rule engine.
- Empty rule sets short-circuit before hitting the engine — they record
a `passed: true, resultCount: 0` history entry so callers still see
the checkpoint was reached.
- The history is bounded; once the length exceeds `historyCap`, the
oldest entries are dropped via `slice(-historyCap)`.