Router
The router handles all the custom logic required to mediate between the lobby, the game, and the player. Its interface required 3 important functions:
The router is meant to be a thin layer. In most cases it will be boilerplate code that can be easily adapted by reference to other projects.
Queue Validation
Players join the queue for a particular game room from the lobby. The lobby records their identity and their staked amount in a QueueEntry:
The current Queue for a given GameRoom is a list of QueueEntrys. The Lobby passes this list to the router, which examines them and decides whether the configuration is valid for a game.
Both of the "Ok" statuses require return of a GameID to be used for session tracking. For our Tic-Tac-Toe game we check that everyone has committed the correct stakes (100 tokens), and that there are exactly 2 players.
We could alternatively just check that all of the staked amounts are equal, to allow for different tiers of game rooms in the lobby. When the router returns #ValidReady
the lobby sends the instruction to start the game with the validated queue.
Game Session Creation
For session-based games this involves creating a new instance of the Game class. The queue is passed through to the game, and the instance is placed in the internal map HashMap<Nat, Game.Game>
for the router.
The game is to handle any initialization steps (player choices, custom skins voting for map, etc) before actually starting gameplay. Now the game can begin and the router has 2 important I/O functions during the session:
Game View
The game state can be encoded however the developer wants for integration on their front-end. For our game we've chosen a simple 9-character string representing the game board. The router looks up the matching game instance based on its ID, and retrieves the view:
For games without perfect knowledge, the router can support player-specific views by passing a Principal.
Input
In the other direction, commands from the player should be structured in whatever way makes most sense for the particular game. In this case the place
function on the game class takes coordinates.
The player takes their turn by sending an input to the game router, which passes it to the appropriate game instance. The router determined the identity of the user and passes it to the game object. The game is responsible for validating the turn.
Game Result
The previous 2 I/O methods are not specified by the architecture but particular to the game. Now we are back to a specific method used by the lobby. The lobby queries the router for the result of a given gameID
:
An existing game session is either still ongoing, or it's ended.
For games that have ended, the router is responsible for providing the final stakes that have been updated by gameplay.
For this case, notice that we've punted responsibility for building the GameSessionResult
object into the Game class itself. That need not always be the case. The game might have its own scoring or ranking system that isn't necessarily 1-to-1 with token balances. Then, the router would take responsibility for translating the game-specific ranking to a GameSessionResult
object with the correct token balances.
The Lobby uses the GameSessionResult
to credit token balances back to the players when the game has finished.
Last updated