Operations
The Operation pattern provides an extensible, testable, replayable way to handle complex game actions. Use it when your game has many distinct operations that need guards, async support, or undo.
When to Use
The Operation pattern is opt-in — for complex games only. Simple games can use engine helpers directly. Consider operations when:
- You have 10+ distinct game actions
- Actions need guard conditions (can this action happen now?)
- You want undo/redo
- Actions have async components (network, animation callbacks)
Registry + Executor
typescript
import { OperationRegistry, OperationExecutor } from "quantum-forge/operations";
const registry = new OperationRegistry();
// Register an operation
registry.register({
key: "move",
canApply: (ctx) => {
const state = ctx.getState();
return state.player.canMove;
},
apply: (ctx) => {
const state = ctx.getState();
state.player.x += ctx.params.dx;
state.player.y += ctx.params.dy;
ctx.setState({ ...state });
return { type: "sync" };
},
});
// Register an async operation
registry.register({
key: "attack",
canApply: (ctx) => ctx.getState().player.energy > 0,
apply: (ctx) => {
const state = ctx.getState();
state.player.energy -= 10;
ctx.setState({ ...state });
return {
type: "deferred",
promise: animateAttack().then(() => {
// Post-animation state update
const s = ctx.getState();
s.enemy.health -= 25;
ctx.setState({ ...s });
}),
};
},
});
// Execute
const executor = new OperationExecutor(registry);
executor.executeSync("move", context); // fire-and-forget deferred
await executor.execute("attack", context); // await deferred promiseOperation Structure
Each operation has:
| Field | Type | Description |
|---|---|---|
key | string | Unique identifier |
canApply | (ctx) => boolean | Guard — can this run now? |
apply | (ctx) => Result | Execute the operation |
The apply function returns:
{ type: "sync" }— completed immediately{ type: "deferred", promise: Promise }— has async follow-up
Context Object
The executor passes a context to each operation. Your game defines the context shape:
typescript
interface GameContext {
getState(): GameState;
setState(state: GameState): void;
params: Record<string, any>;
registry: QuantumRegistry; // quantum access
}Why Operations?
- Extensible — add new operations without modifying core code
- Testable — operations are data, easy to unit test
- Replayable — store operations for undo/replay
- Guarded —
canApplyprevents invalid state transitions - Async-friendly — deferred pattern handles animation callbacks