Asobi

Open-source game backend platform built on Erlang/OTP and the Nova ecosystem.

Asobi provides everything you need to build and run multiplayer games: authentication, player management, real-time multiplayer, matchmaking, leaderboards, virtual economy, social features, and background jobs – all in a single BEAM release.

Features

Quick Start

Prerequisites

Setup

Add asobi as a dependency:

{deps, [
    {asobi, {git, "https://github.com/widgrensit/asobi.git", {branch, "main"}}}
]}.

Configure your sys.config:

[
    {kura, [
        {repo, asobi_repo},
        {host, "localhost"},
        {database, "my_game_dev"},
        {user, "postgres"},
        {password, "postgres"}
    ]},
    {shigoto, [
        {pool, asobi_repo}
    ]},
    {asobi, [
        {plugins, [
            {pre_request, nova_request_plugin, #{
                decode_json_body => true,
                parse_qs => true
            }},
            {pre_request, nova_cors_plugin, #{allow_origins => <<"*">>}},
            {pre_request, nova_correlation_plugin, #{}}
        ]},
        {game_modes, #{
            ~"my_mode" => my_game_module
        }},
        {matchmaker, #{
            tick_interval => 1000,
            max_wait_seconds => 60
        }},
        {session, #{
            token_ttl => 900,
            refresh_ttl => 2592000
        }}
    ]}
].

Start the database and run:

rebar3 shell

Asobi runs migrations automatically on startup.

Implementing a Game

Implement the asobi_match behaviour to define your game logic:

-module(my_arena_game).
-behaviour(asobi_match).

-export([init/1, join/2, leave/2, handle_input/3, tick/1, get_state/2]).

init(Config) ->
    {ok, #{players => #{}, round => 1}}.

join(PlayerId, State) ->
    {ok, State#{players => maps:put(PlayerId, #{score => 0}, maps:get(players, State))}}.

leave(PlayerId, State) ->
    {ok, State#{players => maps:remove(PlayerId, maps:get(players, State))}}.

handle_input(PlayerId, #{~"action" := ~"shoot"} = Input, State) ->
    %% Process player input, update game state
    {ok, State}.

tick(State) ->
    %% Called every tick (default 10/sec) -- advance game simulation
    {ok, State}.

get_state(PlayerId, State) ->
    %% Return the state visible to this player
    maps:get(players, State).

Register your game mode in config:

{asobi, [
    {game_modes, #{~"arena" => my_arena_game}}
]}

Stack

Layer Technology
HTTP / REST Nova (Cowboy)
WebSocket Nova WebSocket (Cowboy)
Database / ORM Kura (PostgreSQL via pgo)
Real-time UI Arizona
Authentication nova_auth
Background Jobs Shigoto
Pub/Sub OTP pg module

Why BEAM?

The BEAM VM is uniquely suited for game backends:

Documentation

Full documentation is available via rebar3 ex_doc.

License

Apache-2.0