Apply actions
applyActions (required)
type ApplyActionsFn<MetaData = Record<string, unknown>> =
(gameState: GameState<MetaData>, action: Action) => void;
Called once per legal action. Mutates gameState in place and returns nothing.
export const applyActions: ApplyActionsFn = (state, action) => {
if (action.type === "SpaceClick") {
action.space.addPiece(`p${Date.now()}`, new XToken({ order: 1 }));
} else if (action.type === "PieceClick") {
action.piece.isSelected = !action.piece.isSelected;
} else if (action.type === "ButtonClick") {
if (action.button.id === "end-turn") passTurn(state);
}
};
The action union
type Action =
| { type: "SpaceClick"; space: Space }
| { type: "PieceClick"; piece: Piece }
| { type: "ButtonClick"; button: Button };
space / piece are the live instances from gameState : see v2-game-state. button is the full Button you returned from getButtons.
An action only reaches you if it was legal: it must have appeared in getAvailableActions for that viewer. You do not need to re-check membership, but you should still validate anything the enumeration can't express.
Multi-step turns
Stash intermediate per-turn state in gameState.metaData. It persists between actions and is visible to everyone.