←back to thread

333 points freetonik | 5 comments | | HN request time: 0s | source
Show context
fleabitdev ◴[] No.42471288[source]
This engine uses a Redux-like architecture. You have a State type (containing data like "the position of the black kingside rook") and a stream of in-game actions (like "knight to F3"). Each action is handled by a pure function which converts the current State to a new State. You can either transmit State deltas from the server to the client, or just transmit the actions themselves (https://longwelwind.net/blog/networking-turn-based-game/).

This design makes it easy to implement optimistic updates, rollback, replays, automated testing, and recovery after a disconnection. It's a surprisingly good fit for UI, too; you can render simple games as a React component which takes the current State as one of its props.

However, a stream of context-free actions can be a really inconvenient representation for some games. The rules of a board game are often like the control flow of a computer program: you'll see branching, iteration, local variables, function calls, structured concurrency, and sometimes even race conditions and reentrancy. When you try to represent all of this logic as a State object, you're basically maintaining a snapshot of a "call stack" as plain data, and manually resuming that "program" whenever you handle an action. It doesn't seem ideal.

I've been sketching a board game engine which would represent the game logic as normal code instead. It seems promising, but it really needs a couple of language features which don't exist in the mainstream yet, like serialisation of suspended async functions.

replies(5): >>42471791 #>>42472084 #>>42472570 #>>42475998 #>>42477605 #
1. Savageman ◴[] No.42475998[source]
How does secret state fit in this? If you want each player hand to be secret, then each player has its own state?
replies(1): >>42476225 #
2. fleabitdev ◴[] No.42476225[source]
boardgame.io only runs game logic on the server, and it censors the State just before sending it to each client. This strategy makes the UI feel less responsive, but it keeps things simple.

The Swords and Ravens blog post recommends resolving actions on the client when they don't require secret information, but resolving other actions on the server. You'd also need to resolve actions on the server when they involve RNG.

replies(2): >>42476298 #>>42478488 #
3. Savageman ◴[] No.42476298[source]
Interesting, would you share the link of this post please?
replies(1): >>42476330 #
4. fleabitdev ◴[] No.42476330{3}[source]
https://longwelwind.net/blog/networking-turn-based-game/
5. Longwelwind ◴[] No.42478488[source]
I don't recommend resolving actions on the server in any situation:

For actions that require secret information, you would filter the actions sent to the client of any secret information and make sure the code handling the action can handle both the action and the filtered actins.

For actions involving RNG, make all randomness rely on a seed. This seed would be stored server-side and passed along the action when sent to the client. This makes sure the clients can deterministically reproduce the update.