GameServer.Leaderboards (GameServer v1.0.509)
The Leaderboards context.
Provides server-authoritative leaderboard management. Scores can only be submitted via server-side code — there is no public API for score submission.
Usage
# Create a leaderboard
{:ok, lb} = Leaderboards.create_leaderboard(%{
slug: "weekly_kills",
title: "Weekly Kills",
sort_order: :desc,
operator: :incr
})
# Submit score (server-only): resolve the active leaderboard first and submit by integer ID
leaderboard = Leaderboards.get_active_leaderboard_by_slug("weekly_kills")
{:ok, record} = Leaderboards.submit_score(leaderboard.id, user_id, 10)
# List records with rank (use integer leaderboard id)
records = Leaderboards.list_records(leaderboard.id, page: 1, limit: 25)
# Get user's record (use integer leaderboard id)
{:ok, record} = Leaderboards.get_user_record(leaderboard.id, user_id)
Summary
Functions
Returns a changeset for a leaderboard (used in forms).
Returns a changeset for a record (used in admin forms).
Count all leaderboard records across all leaderboards.
Counts unique leaderboard slugs.
Counts leaderboards matching the given filters.
Counts records for a leaderboard.
Creates a new leaderboard.
Deletes a leaderboard and all its records.
Deletes a record.
Deletes a user's record from a leaderboard. Accepts either leaderboard ID (integer) or slug (string).
Ends a leaderboard by setting ends_at to the current time.
Gets the currently active leaderboard with the given slug.
Returns nil if no active leaderboard exists.
Gets a leaderboard by its integer ID.
Gets a leaderboard by its integer ID. Raises if not found.
Gets a single record by leaderboard ID and user ID.
Gets a record by its integer ID. Raises if not found.
Gets a user's record with their rank.
Returns {:ok, record_with_rank} or {:error, :not_found}.
Lists unique leaderboard slugs with summary info.
Lists leaderboards with optional filters.
Lists all leaderboards with the given slug (all seasons), ordered by end date.
Lists records for a leaderboard, ordered by rank.
Lists records around a specific user (centered on their position).
Submits a score for a user on a leaderboard.
Updates an existing leaderboard.
Updates an existing record.
Functions
@spec change_leaderboard(GameServer.Leaderboards.Leaderboard.t(), map()) :: Ecto.Changeset.t()
Returns a changeset for a leaderboard (used in forms).
@spec change_record(GameServer.Leaderboards.Record.t(), map()) :: Ecto.Changeset.t()
Returns a changeset for a record (used in admin forms).
@spec count_all_records() :: non_neg_integer()
Count all leaderboard records across all leaderboards.
@spec count_leaderboard_groups() :: non_neg_integer()
Counts unique leaderboard slugs.
@spec count_leaderboards(keyword()) :: non_neg_integer()
Counts leaderboards matching the given filters.
Accepts the same filter options as list_leaderboards/1.
@spec count_records(integer()) :: non_neg_integer()
Counts records for a leaderboard.
@spec create_leaderboard(GameServer.Types.leaderboard_create_attrs()) :: {:ok, GameServer.Leaderboards.Leaderboard.t()} | {:error, Ecto.Changeset.t()}
Creates a new leaderboard.
Attributes
See GameServer.Types.leaderboard_create_attrs/0 for available fields.
Examples
iex> create_leaderboard(%{slug: "my_lb", title: "My Leaderboard"})
{:ok, %Leaderboard{}}
iex> create_leaderboard(%{slug: "", title: ""})
{:error, %Ecto.Changeset{}}
@spec delete_leaderboard(GameServer.Leaderboards.Leaderboard.t()) :: {:ok, GameServer.Leaderboards.Leaderboard.t()} | {:error, Ecto.Changeset.t()}
Deletes a leaderboard and all its records.
@spec delete_record(GameServer.Leaderboards.Record.t()) :: {:ok, GameServer.Leaderboards.Record.t()} | {:error, Ecto.Changeset.t()}
Deletes a record.
@spec delete_user_record(integer() | String.t(), integer()) :: {:ok, GameServer.Leaderboards.Record.t()} | {:error, :not_found}
Deletes a user's record from a leaderboard. Accepts either leaderboard ID (integer) or slug (string).
@spec end_leaderboard( GameServer.Leaderboards.Leaderboard.t() | integer() | String.t() ) :: {:ok, GameServer.Leaderboards.Leaderboard.t()} | {:error, Ecto.Changeset.t() | :not_found}
Ends a leaderboard by setting ends_at to the current time.
@spec get_active_leaderboard_by_slug(String.t()) :: GameServer.Leaderboards.Leaderboard.t() | nil
Gets the currently active leaderboard with the given slug.
Returns nil if no active leaderboard exists.
An active leaderboard is one that:
- Has not ended (
ends_atis nil or in the future) - Has started (
starts_atis nil or in the past)
If multiple active leaderboards exist with the same slug, returns the most recently created one.
@spec get_leaderboard(integer() | String.t()) :: GameServer.Leaderboards.Leaderboard.t() | nil
Gets a leaderboard by its integer ID.
Examples
iex> get_leaderboard(123)
%Leaderboard{id: 123}
iex> get_leaderboard(999)
nil
@spec get_leaderboard!(integer()) :: GameServer.Leaderboards.Leaderboard.t()
Gets a leaderboard by its integer ID. Raises if not found.
@spec get_record(integer(), integer()) :: GameServer.Leaderboards.Record.t() | nil
Gets a single record by leaderboard ID and user ID.
@spec get_record!(integer()) :: GameServer.Leaderboards.Record.t()
Gets a record by its integer ID. Raises if not found.
Intended for internal/admin usage.
@spec get_user_record(integer(), integer()) :: {:ok, GameServer.Leaderboards.Record.t()} | {:error, :not_found}
Gets a user's record with their rank.
Returns {:ok, record_with_rank} or {:error, :not_found}.
Lists unique leaderboard slugs with summary info.
Returns a list of maps with:
:slug- the leaderboard slug:title- title from the latest leaderboard:description- description from the latest leaderboard:active_id- ID of the currently active leaderboard (or nil):latest_id- ID of the most recent leaderboard:season_count- total number of leaderboards with this slug
@spec list_leaderboards(keyword()) :: [GameServer.Leaderboards.Leaderboard.t()]
Lists leaderboards with optional filters.
Options
:slug- Filter by slug (returns all seasons of that leaderboard):active- Iftrue, only active leaderboards. Iffalse, only ended.:order_by- Order by field::ends_ator:inserted_at(default):starts_after- Only leaderboards that started after this DateTime:starts_before- Only leaderboards that started before this DateTime:ends_after- Only leaderboards that end after this DateTime:ends_before- Only leaderboards that end before this DateTime:page- Page number (default 1):page_size- Page size (default 25)
Examples
iex> list_leaderboards(active: true)
[%Leaderboard{}, ...]
iex> list_leaderboards(slug: "weekly_kills")
[%Leaderboard{}, ...]
iex> list_leaderboards(starts_after: ~U[2025-01-01 00:00:00Z])
[%Leaderboard{}, ...]
@spec list_leaderboards_by_slug( String.t(), keyword() ) :: [GameServer.Leaderboards.Leaderboard.t()]
Lists all leaderboards with the given slug (all seasons), ordered by end date.
@spec list_records(integer(), GameServer.Types.pagination_opts()) :: [ GameServer.Leaderboards.Record.t() ]
Lists records for a leaderboard, ordered by rank.
Options
See GameServer.Types.pagination_opts/0 for available options.
Returns records with rank field populated.
@spec list_records_around_user(integer(), integer(), keyword()) :: [ GameServer.Leaderboards.Record.t() ]
Lists records around a specific user (centered on their position).
Returns records above and below the user's rank.
Options
:limit- Total number of records to return (default 11, centered on user)
@spec submit_score(integer(), integer(), integer(), map()) :: {:ok, GameServer.Leaderboards.Record.t()} | {:error, term()}
Submits a score for a user on a leaderboard.
This is a server-only function — there is no public API for score submission. The score is processed according to the leaderboard's operator:
:set— Always replace with new score:best— Only update if new score is better (respects sort_order):incr— Add to existing score:decr— Subtract from existing score
To submit to a leaderboard by slug, first get the active leaderboard ID:
leaderboard = Leaderboards.get_active_leaderboard_by_slug("weekly_kills")
Leaderboards.submit_score(leaderboard.id, user_id, 10)Examples
iex> submit_score(123, user_id, 10)
{:ok, %Record{score: 10}}
iex> submit_score(123, user_id, 5, %{weapon: "sword"})
{:ok, %Record{score: 15, metadata: %{weapon: "sword"}}}
@spec update_leaderboard( GameServer.Leaderboards.Leaderboard.t(), GameServer.Types.leaderboard_update_attrs() ) :: {:ok, GameServer.Leaderboards.Leaderboard.t()} | {:error, Ecto.Changeset.t()}
Updates an existing leaderboard.
Note: slug, sort_order, and operator cannot be changed after creation.
Attributes
See GameServer.Types.leaderboard_update_attrs/0 for available fields.
@spec update_record(GameServer.Leaderboards.Record.t(), map()) :: {:ok, GameServer.Leaderboards.Record.t()} | {:error, Ecto.Changeset.t()}
Updates an existing record.
Intended for internal/admin usage.