Divi Games

Courses / Build a game

The GameModule trait

A game on the side-chain is one Rust type that implements GameModule (dg_core::module):

pub trait GameModule: Send + Sync {
    fn id(&self) -> &'static str;
    fn apply(&self, ctx: &mut dyn Ctx, sender: &str, op: &serde_json::Value) -> Result<(), ActionError>;
    fn view(&self, state: &dyn StateRead, query: &Query) -> Result<serde_json::Value, ActionError>;
    fn end_block(&self, _ctx: &mut dyn Ctx) -> Result<(), ActionError> { Ok(()) }   // optional
}

How a move reaches your code

A player signs a transaction whose action is:

{ "type": "game", "game": "tictactoe", "op": { "kind": "move", "id": "00000000000000a1", "cell": 4 } }

The executor looks up the module whose id() is tictactoe and calls apply(ctx, sender, op). The reserved ids market, htlc, backend, certificate, bridge and system belong to system modules and cannot be used by a game.

Registering a game

A new game is one file in crates/dg-games/src/ plus one line in dg_games::all():

pub fn all() -> Vec<Box<dyn dg_core::GameModule>> {
    vec![Box::new(TicTacToe), Box::new(Blackjack), Box::new(TowerDefense)]
}

The one rule

apply must be deterministic. Every node replays the chain and must reach the same state. No clock, no random numbers, no floating point, no HashMap iteration order. Use what Ctx gives you instead (ctx.height(), ctx.block_time_ms(), ctx.public_random(...)).