asobi_vote_server (asobi v0.35.4)
View SourceVote lifecycle state machine.
Manages a single vote instance within a match. States flow open -> closed
(with automatic resolution on close). Supports plurality and approval voting
methods, configurable timed windows, live or hidden tallies, veto, and
tie-breaking.
Config keys
| Key | Type | Default | Description |
|---|---|---|---|
match_id | binary() | required | Parent match ID |
match_pid | pid() | required | Match server process |
options | [map()] | required | List of #{id, label} option maps |
eligible | [binary()] | [] | Eligible voter IDs |
window_ms | pos_integer() | 15000 | Vote window in milliseconds |
method | binary() | "plurality" | "plurality", "approval", "weighted", or "ranked" |
visibility | binary() | "live" | "live" or "hidden" |
tie_breaker | binary() | "random" | "random" or "first" |
veto_enabled | boolean() | false | Allow eligible voters to veto |
template | binary() | "default" | Template name (resolved from app config if defined) |
vote_id | binary() | auto-generated | Override vote ID |
weights | map() | #{} | Voter weights for "weighted" method: #{voter_id => number()} |
max_revotes | pos_integer() | 3 | Max times a voter can change their vote |
window_type | binary() | "fixed" | "fixed", "ready_up", "hybrid", or "adaptive" |
min_window_ms | pos_integer() | 5000 | Minimum window for "hybrid" mode |
supermajority | float() | 0.75 | Threshold for "adaptive" early close |
require_supermajority | boolean() | false | If true, winner must reach supermajority threshold or result is no-consensus |
spectators | [binary()] | [] | Spectator voter IDs (separate pool) |
spectator_weight | float() | 0.3 | Weight ratio for spectator votes (0.0-1.0) |
quorum | float() | 0.0 | Min fraction of eligible voters needed (0.0 = disabled) |
default_votes | map() | #{} | Default option per voter if they don't vote: #{voter_id => option_id} |
delegation | map() | #{} | Vote delegation: #{delegator_id => delegate_id} |
Vote templates
Define reusable templates in app config. Per-call config overrides template defaults:
{asobi, [{vote_templates, #{
~"boon_pick" => #{method => ~"plurality", window_ms => 15000, visibility => ~"live"},
~"path_choice" => #{method => ~"approval", window_ms => 20000, visibility => ~"hidden"}
}}]}Vote methods
- Plurality: each voter picks one option, highest count wins.
- Approval: each voter submits a list of approved option IDs, highest approval count wins.
- Weighted: like plurality, but each vote is multiplied by the voter's
weight from the
weightsmap. Unweighted voters default to 1. - Ranked: each voter submits a ranked list
[first_choice, second, ...]. Iterative elimination: lowest first-choice option eliminated, its votes redistributed to next preference, until one option has majority.
Spectator voting
Spectators are a separate voter pool. Their tallies are merged with player
tallies using spectator_weight (default 0.3 = 30% influence). Set
spectators list and optionally spectator_weight.
Async voting
For non-real-time games. Set quorum (0.0-1.0) to resolve early when
enough voters participate. Use default_votes for absent players and
delegation to let a player's vote follow another's.
Window types
- Fixed (default): vote runs for exactly
window_ms, then closes. - Ready-up: closes as soon as all eligible voters have cast a vote,
or when
window_msexpires (whichever comes first). - Hybrid: like ready-up, but enforces a minimum
min_window_msbefore early close is allowed. Prevents snap decisions. - Adaptive: starts with
window_ms, but when a supermajority threshold is reached, the remaining time shrinks to 3 seconds (giving others a last chance). Resets if supermajority is lost.
Grace period
Late votes arriving within 500ms after the window closes are still accepted to compensate for network latency.
Summary
Functions
Veto the vote (immediately cancels it). Only works if veto_enabled is true.
Cast a vote. Replaces any previous vote by the same voter during the window.
Return the current vote state including status, options, tallies (if live), and time remaining.
Start a vote server with the given config. See moduledoc for config keys.
Functions
-spec callback_mode() -> gen_statem:callback_mode_result().
Veto the vote (immediately cancels it). Only works if veto_enabled is true.
Cast a vote. Replaces any previous vote by the same voter during the window.
-spec closed(gen_statem:event_type() | enter, term(), map()) -> gen_statem:state_enter_result(atom()).
Return the current vote state including status, options, tallies (if live), and time remaining.
-spec open(gen_statem:event_type() | enter, term(), map()) -> gen_statem:state_enter_result(atom()).
-spec start_link(map()) -> gen_statem:start_ret().
Start a vote server with the given config. See moduledoc for config keys.