asobi_zone_manager (asobi v0.72.5)

View Source

Lazy zone lifecycle manager for the world server.

Replaces the static zone_pids map with on-demand zone creation and idle reaping. For large worlds (2000x2000 grids), spawning all zones at startup is untenable — this module creates zones on first access and reaps them after an idle timeout.

Hot-path lookups go through ETS directly, bypassing the gen_server.

Summary

Functions

Return existing zone or start a new one. ETS fast path first.

As ensure_zone/2, with an explicit call timeout. Callers on a hot path that must not stall on a busy manager (asobi_zone's per-tick crossing check, widgrensit/asobi#271) pass a tighter bound than the gen_server default and treat a timeout as "not available this tick".

Return all active zone pids. For the ticker.

Non-creating lookup. ETS only.

Spawn all zones in grid. Backward compat for small grids.

Register an externally-spawned zone with the manager.

Hint that zone can be unloaded.

Replace a zone that died under a caller holding its pid.

Provide per-coord initial zone_state. Used to thread per-zone state from GameMod:generate_world/2 (e.g. lua_state for the asobi_lua_world bridge, or station/planet seeds for sc_game) through to each zone's init. Without this, zones would all start with empty zone_state and any callback that needs per-zone setup would silently no-op.

Update the base zone config used when spawning new zones.

Start the zone manager.

Reset idle timer for a zone. Fire-and-forget.

Called by zone on terminate. Cleans up ETS entry.

Functions

ensure_zone(Ref, Coords)

-spec ensure_zone(pid() | atom(), {integer(), integer()}) ->
                     {ok, pid(), created | existing} | {error, term()}.

Return existing zone or start a new one. ETS fast path first.

The third element of a successful result tells the caller whether this call is what brought the zone into existence (created) or whether it was already running (existing) - callers that need to backfill subscribers whose interest ring already covered these coords (widgrensit/asobi#275) only need to act on created.

ensure_zone(Ref, Coords, Timeout)

-spec ensure_zone(pid() | atom(), {integer(), integer()}, timeout()) ->
                     {ok, pid(), created | existing} | {error, term()}.

As ensure_zone/2, with an explicit call timeout. Callers on a hot path that must not stall on a busy manager (asobi_zone's per-tick crossing check, widgrensit/asobi#271) pass a tighter bound than the gen_server default and treat a timeout as "not available this tick".

get_active_zones/1

-spec get_active_zones(pid() | atom()) -> [pid()].

Return all active zone pids. For the ticker.

get_zone(Ref, Coords)

-spec get_zone(pid() | atom(), {integer(), integer()}) -> {ok, pid()} | not_loaded.

Non-creating lookup. ETS only.

handle_call/3

-spec handle_call(term(), gen_server:from(), map()) -> {reply, term(), map()}.

handle_cast/2

-spec handle_cast(term(), map()) -> {noreply, map()}.

handle_info/2

-spec handle_info(term(), map()) -> {noreply, map()}.

init(Opts)

-spec init(map()) -> {ok, map()}.

pre_warm(Ref)

-spec pre_warm(pid() | atom()) -> ok.

Spawn all zones in grid. Backward compat for small grids.

register_zone(Ref, Coords, ZonePid)

-spec register_zone(pid() | atom(), {integer(), integer()}, pid()) -> ok.

Register an externally-spawned zone with the manager.

release_zone(Ref, Coords)

-spec release_zone(pid() | atom(), {integer(), integer()}) -> ok.

Hint that zone can be unloaded.

revive_zone(Ref, Coords, DeadPid)

-spec revive_zone(pid() | atom(), {integer(), integer()}, pid()) ->
                     {ok, pid(), created | existing} | {error, term()}.

Replace a zone that died under a caller holding its pid.

ensure_zone/2 hands out a pid without a lease on it, so a genuinely-idle zone can still be reaped in the gap between the lookup and the caller proving it occupied (widgrensit/asobi#283). The caller that notices this cannot just retry ensure_zone/2: the ETS slot still points at the dead pid until the manager has processed its DOWN, so the retry would hand back the same corpse. This waits for that DOWN (the zone's terminate/2 snapshot has already run by then, so the replacement loads current state) and starts a fresh zone.

Returns the already-live zone when someone else got there first.

set_initial_zone_states(Ref, ZoneStates)

-spec set_initial_zone_states(pid() | atom(), map()) -> ok.

Provide per-coord initial zone_state. Used to thread per-zone state from GameMod:generate_world/2 (e.g. lua_state for the asobi_lua_world bridge, or station/planet seeds for sc_game) through to each zone's init. Without this, zones would all start with empty zone_state and any callback that needs per-zone setup would silently no-op.

set_zone_config(Ref, Config)

-spec set_zone_config(pid() | atom(), map()) -> ok.

Update the base zone config used when spawning new zones.

start_link(Opts)

-spec start_link(map()) -> gen_server:start_ret().

Start the zone manager.

terminate/2

-spec terminate(term(), map()) -> ok.

touch_zone(Ref, Coords)

-spec touch_zone(pid() | atom(), {integer(), integer()}) -> ok.

Reset idle timer for a zone. Fire-and-forget.

zone_terminated(Ref, Coords, ZonePid)

-spec zone_terminated(pid() | atom(), {integer(), integer()}, pid()) -> ok.

Called by zone on terminate. Cleans up ETS entry.