first commit
This commit is contained in:
@@ -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)`.
|
||||
Reference in New Issue
Block a user