asobi_world_server (asobi v0.72.5)
View SourceA persistent, zoned world: the gen_statem behind large session games
(game_type = "world"). It partitions space into a grid of asobi_zone
processes, moves players between zones as they travel, runs a world-level
post_tick, and drives voting. Use it for MMO-style shared spaces; for
transient matches use asobi_match_server instead.
Summary
Types
What a non-member may see. Every key is optional: maps:with/2 keeps only what the world set.
Functions
Projection of get_info/1 for callers who are not members of the world.
As move_player/3, but for a caller that already holds the entity's full
state (asobi_zone's resolve_zone_crossings/1). Entity is written into the
target zone as-is instead of being reconstructed as #{x, y, type} only, so
fields a game script keeps beyond position - health, inventory, whatever -
survive the zone boundary. See widgrensit/asobi#248.
As pos_to_zone/2, additionally clamped to 0 .. GridSize - 1 on both axes.
Use this wherever a player's current zone is derived from position. See
widgrensit/asobi#248.
Tell the world server a zone was just created by something other than a
player join or crossing - asobi_zone creating a neighbour to receive a
crossing NPC (widgrensit/asobi#271). The world server owns player_zones,
so only it can subscribe the already-connected players whose interest ring
already covered those coords (widgrensit/asobi#275).
Types
-type listing() :: #{world_id => binary(), status => atom(), player_count => non_neg_integer(), max_players => pos_integer(), mode => binary() | undefined, grid_size => non_neg_integer(), started_at => integer() | undefined, phase => map()}.
What a non-member may see. Every key is optional: maps:with/2 keeps only what the world set.
Functions
-spec callback_mode() -> gen_statem:callback_mode_result().
-spec cancel(pid()) -> ok.
-spec finished(gen_statem:event_type() | enter, term(), map()) -> gen_statem:state_enter_result(atom()).
As join/3, plus an opaque join context from the client. asobi does not
interpret it; it reaches the game module's join/3 if it exports one.
Projection of get_info/1 for callers who are not members of the world.
get_info/1 carries the full players roster. Discovery and the join
reply are open to any authenticated player, so they get identity, mode,
status, capacity and phase only.
The projection descends into phase: asobi_phase:info/1 evolves
independently and its config is game-authored, so allowlisting only the
top level would leak whatever a future phase field happens to carry.
-spec loading(gen_statem:event_type() | enter, term(), map()) -> gen_statem:state_enter_result(atom()) | gen_statem:event_handler_result(atom()).
As move_player/3, but for a caller that already holds the entity's full
state (asobi_zone's resolve_zone_crossings/1). Entity is written into the
target zone as-is instead of being reconstructed as #{x, y, type} only, so
fields a game script keeps beyond position - health, inventory, whatever -
survive the zone boundary. See widgrensit/asobi#248.
-spec pos_to_zone({number(), number()}, non_neg_integer(), pos_integer()) -> {non_neg_integer(), non_neg_integer()}.
As pos_to_zone/2, additionally clamped to 0 .. GridSize - 1 on both axes.
Use this wherever a player's current zone is derived from position. See
widgrensit/asobi#248.
-spec post_tick(pid(), non_neg_integer()) -> ok.
-spec running(gen_statem:event_type() | enter, term(), map()) -> gen_statem:state_enter_result(atom()).
-spec start_link(map()) -> gen_statem:start_ret().
Tell the world server a zone was just created by something other than a
player join or crossing - asobi_zone creating a neighbour to receive a
crossing NPC (widgrensit/asobi#271). The world server owns player_zones,
so only it can subscribe the already-connected players whose interest ring
already covered those coords (widgrensit/asobi#275).