asobi_match behaviour (asobi v0.35.4)
View SourceThe behaviour a game developer implements to define server-authoritative game logic. Each game mode is a module that implements this behaviour; Asobi runs one process per match and invokes these callbacks at the right moments in the match lifecycle.
The Lua runtime (asobi_lua) implements this behaviour on your behalf, so
a Lua match.lua and an Erlang asobi_match module are the same contract
on two surfaces. Most games are written in Lua; implement this behaviour
directly when you want behaviour-level control or are embedding Asobi in an
existing OTP application.
Example
-module(my_card_game).
-behaviour(asobi_match).
-export([init/1, join/2, leave/2, handle_input/3, tick/1, get_state/2]).
init(Config) -> {ok, #{deck => shuffle(Config), players => #{}}}.
join(PlayerId, State) -> {ok, State#{players => add(PlayerId, State)}}.
leave(_PlayerId, State) -> {ok, State}.
handle_input(_P, _I, S) -> {ok, S}.
tick(State) -> {ok, State}.
get_state(_PlayerId, S) -> S.Required: init/1, join/2, leave/2, handle_input/3, and
exactly one of get_state/2 or get_state/1. Every other callback is
optional (see optional_callbacks).
Summary
Callbacks
Shared-payload variant of get_state/2. Avoids the per-player encode cost
when every player sees the same world. Mutually exclusive with
get_state/2 - export exactly one.
Project the full match state into what this player should see. Hide opponent hands, out-of-sight positions, and hidden rolls here.
A player action arrived over the WebSocket. Validate and apply it. Inputs are serialised onto the match process, so state can be mutated here without races.
Called once when the match is created, with the mode config it was started with. Returns the initial game state, which is threaded through every other callback.
A player is joining. Accept and attach them, returning the new state, or
reject with {error, Reason} (for example when the match is already full or
in progress).
A player disconnected or was removed. Cannot fail. Use it to release reservations, stop timers, or forfeit.
Optional. Hook invoked when a declared phase ends.
Optional. Hook invoked when a declared phase begins.
Optional. Declare named, timed phases (lobby, active, results). Supported for Erlang match games and Lua world games.
Called on a fixed interval (default 10 Hz, configurable per mode). Advance
time, run AI, and check win conditions. Return {finished, Result, State}
to end the match. Optional: omit it if your game has no fixed time step.
Optional. Return a vote configuration to open an in-match vote, or none.
Optional. React to a resolved vote; Result carries the winning option.
Callbacks
Shared-payload variant of get_state/2. Avoids the per-player encode cost
when every player sees the same world. Mutually exclusive with
get_state/2 - export exactly one.
Project the full match state into what this player should see. Hide opponent hands, out-of-sight positions, and hidden rolls here.
-callback handle_input(PlayerId :: binary(), Input :: map(), GameState :: term()) -> {ok, GameState1 :: term()} | {error, Reason :: term()}.
A player action arrived over the WebSocket. Validate and apply it. Inputs are serialised onto the match process, so state can be mutated here without races.
Called once when the match is created, with the mode config it was started with. Returns the initial game state, which is threaded through every other callback.
-callback join(PlayerId :: binary(), GameState :: term()) -> {ok, GameState1 :: term()} | {error, Reason :: term()}.
A player is joining. Accept and attach them, returning the new state, or
reject with {error, Reason} (for example when the match is already full or
in progress).
A player disconnected or was removed. Cannot fail. Use it to release reservations, stop timers, or forfeit.
Optional. Hook invoked when a declared phase ends.
-callback on_phase_started(PhaseName :: binary(), GameState :: term()) -> {ok, GameState1 :: term()}.
Optional. Hook invoked when a declared phase begins.
-callback phases(Config :: map()) -> [asobi_phase:phase_def()].
Optional. Declare named, timed phases (lobby, active, results). Supported for Erlang match games and Lua world games.
-callback tick(GameState :: term()) -> {ok, GameState1 :: term()} | {finished, Result :: map(), GameState1 :: term()}.
Called on a fixed interval (default 10 Hz, configurable per mode). Advance
time, run AI, and check win conditions. Return {finished, Result, State}
to end the match. Optional: omit it if your game has no fixed time step.
Optional. Return a vote configuration to open an in-match vote, or none.
-callback vote_resolved(Template :: binary(), Result :: map(), GameState :: term()) -> {ok, GameState1 :: term()}.
Optional. React to a resolved vote; Result carries the winning option.