Upgrade a game
Follow these steps to move a framework v1 or v2 game to this framework. upgrade_framework answers this section when it moves a game here.
This is a rewrite of the game logic and the client, not an edit. Port one rule at a time. Keep the old files to read from until step 5.
Use what Boardweaver provides
Boardweaver provides features that a framework v1 or v2 game built for itself. When the old game has its own version of one, delete its code and its state. Use the feature in the table below instead.
| The old game built its own | Use this instead |
|---|---|
Undo: an undo stack, saved snapshots, Immer patches or an undo intent |
table.allow_undo() in the game logic, and the Undo button from useUndo() in the client (Changing the state) |
| A move made in parts: selection state, tentative moves or arrange-then-commit in the client | A decision of several steps: choice offers, then a confirm offer. The client shows the steps so far with useDraft() (Offers, intents and decisions) |
Match history: a move log, a last-move banner, a replay, or a list of moves kept in metaData |
table.log(line) on each step (Changing the state). Boardweaver shows each step to the other players with its log lines, gives Back, Forward and Skip to now, and replays a finished match. The client reads the lines with useLastChange() and the board's place with usePlayback() (The React client) |
| Move animations from a diff of the old and new state | useBoardTransition() pairs viewer-visible endpoints in either direction. Use useTransitionEffect for custom timing and cleanup, or optional measured overlays (Directional animation) |
| Hidden hand order: shuffling ids, decoy pieces or sorting tricks | .collection() on the space, .hiding() on the piece and .revealing() on the offer (Hidden information) |
| Random numbers: a seed or a generator kept in the state | The rng argument of setup and apply_action (Randomness and determinism) |
Bots: extra seats added in preGameInitialization, or bot moves picked in the client or on a timer |
A Bot in the game logic. The lobby seats it (Bots) |
| Player names and avatars kept in the state | usePlayers() |
| A queue of moves on their way, or an offline banner | usePending(), and useFailures() for refused moves |
| An end screen built into the board | Boardweaver's own end screen, or a GameOver export with useMatchEnd() (The React client) |
| Rules, sound, full screen or leave buttons drawn by the client | Nothing: Boardweaver draws them in the bar under the board |

What is new
| Framework v1 or v2 | This framework |
|---|---|
Game logic in TypeScript, in /src/game.ts |
Game logic in Rust, in /src/lib.rs and the modules it declares |
gameConfig and preGameInitialization |
setup |
getAvailableActions or getSelectableItems, and getButtons |
offers: one Offer per legal step, each an action, an intent and an optional label |
applyActions changes the state in place |
apply_action changes the state only through Table, and refuses a move with Err |
getPlayerScores, scoreLabels, isGameOver |
scores and is_game_over; the client names the score columns (Scores) |
PieceDef and SpaceDef classes, each with a kind |
Piece::new(public_kind) and Space::new(space_id, kind) values |
A piece has a pieceId |
A piece has no id: it is named by its space and its index there |
static constants of a kind |
A const or a data file in /src/data/ |
publicState, privateState |
public_state, private_state, the piece's owner, and private_kind for a hidden face |
metaData, activePlayerIds |
meta_data, active_player_ids |
v1: Boardweaver draws from render(). v2: useGameState, useAvailableActions and match.click |
useBoard, useOffers and useAct in /src/frontend.tsx |
These carry over unchanged: uploaded art (the client reads it with useImage(key)), /theme.json, the rules pages and the marketing page.
1. Read the new contract
Read How a game works. Call get_example with name hexbloom, then a listed path for each file you need. Find the five game functions in /src/lib.rs. Find where /src/frontend.tsx takes an offer with useAct().
2. Map the state
Write down how each def, bucket and map of the old game becomes a space, a piece or a map of the new state. Give every field the old game read or wrote a place there. Find a piece the old code found by pieceId by its space and index, or by a field in public_state. Decide who owns each private part. Leave out every field that only served a feature in Use what Boardweaver provides, such as an undo stack, a move log or a random seed.
References: The state, Hidden information.
3. Port the game logic
Write /src/lib.rs with the five game functions and export_game!. Port the old rules one at a time. Write a #[test] for each rule as you port it. Draw randomness only from the rng argument.
Run validate_code until it answers no Rust error and its tests counts a passed test for each rule. Then play a start_game session to the end at each seat count.
References: The game functions, Changing the state, Offers, intents and decisions, Randomness and determinism, Testing a game.
4. Rewrite the client
Rewrite /src/frontend.tsx against boardweaver/react of this framework. Draw from useBoard(), make only the offers of useOffers() clickable, and take them with useAct().
Run validate_code until it answers no error that names /src/frontend.tsx.
References: The React client, boardweaver/react reference.
5. Delete the old game logic
Delete /src/game.ts, /src/pieces/, /src/spaces/ and every other TypeScript file that only the old game logic imported. Run validate_code until it answers valid: true.
6. Commit
Call commit. The commit builds against this framework and pins the game to it.
Tips
- Port the rules, not the code. TypeScript that mutates a piece found by id does not translate line by line: rewrite each rule against
Table. - A move that took one v2 action and picked several values is several steps here, one value each. Mark the last step
confirm. - Mark every offer whose result depends on hidden information with
.revealing()(Reveals). - Read a seat's position with
state.seat_of(id). APlayerIdis not a seat number (Players). - To stay on the old framework, call
upgrade_frameworkwith the game's committed major before you commit. It removes the pending upgrade, and your file changes stay.
Upgrade animation code
- Replace each diff or snapshot match of two boards with
useBoardTransition(). Animate when itsoutcomeisanimate. Show the new board at once when it issnap. - Draw each piece from its endpoint's
piece. Draw aHiddenspot as a card back at the group's anchor. - Keep your art, paths, easing and effects. Run them with
useTransitionEffectorTransitionProvideroverlays (Optional animation helpers). - Scale every duration and delay with
run.milliseconds. Return a cleanup that cancels everything your renderer starts. - Test each transition for each viewer, direction and interruption, as Client checks lists. Run the tests through the real timeline.