GameServer.Hooks behaviour (GameServer v1.0.860)

Copy Markdown

Behaviour for application-level hooks / callbacks.

Implement this behaviour to receive lifecycle events from core flows (registration, login, provider linking, deletion) and run custom logic.

A module implementing this behaviour can be configured with

config :game_server_core, :hooks_module, MyApp.HooksImpl

The default implementation is a no-op.

Summary

Types

Options passed to hooks that accept an options map/keyword list.

Functions

Call an arbitrary function exported by the configured hooks module.

When a hooks function is executed via call/3 or internal_call/3, an optional :caller can be provided in the options. The caller will be injected into the spawned task's process dictionary and is accessible via GameServer.Hooks.caller/0 (the raw value) or caller_id/0 (the numeric id when the value is a user struct or map containing :id).

Return the user struct for the current caller when available. This will attempt to resolve the caller via GameServer.Accounts.get_user!/1 when the caller is an integer id or a map containing an :id key. Returns nil when no caller or user is found.

Return a list of exported functions on the currently registered hooks module.

Call an internal lifecycle callback. When a callback is missing this returns a sensible default (eg. {:ok, attrs} for before callbacks) so domain code doesn't need to handle missing hooks specially in most cases.

Returns the set of internal lifecycle hook names that are not callable through the public RPC interface.

Invoke a dynamic hook function by name.

Return the configured module that implements the hooks behaviour.

Types

hook_result(attrs_or_user)

@type hook_result(attrs_or_user) :: {:ok, attrs_or_user} | {:error, term()}

kv_opts()

@type kv_opts() :: map() | keyword()

Options passed to hooks that accept an options map/keyword list.

Common keys include :user_id (pos_integer) and other domain-specific options. Hooks may accept either a map or keyword list for convenience.

Callbacks

after_achievement_unlocked(integer, t)

@callback after_achievement_unlocked(integer(), GameServer.Achievements.Achievement.t()) ::
  any()

after_chat_message(t)

@callback after_chat_message(GameServer.Chat.Message.t()) :: any()

after_group_create(t)

@callback after_group_create(GameServer.Groups.Group.t()) :: any()

after_group_delete(t)

@callback after_group_delete(GameServer.Groups.Group.t()) :: any()

after_group_join(integer, t)

@callback after_group_join(integer(), GameServer.Groups.Group.t()) :: any()

after_group_kick(integer, integer, integer)

@callback after_group_kick(integer(), integer(), integer()) :: any()

after_group_leave(integer, integer)

@callback after_group_leave(integer(), integer()) :: any()

after_group_update(t)

@callback after_group_update(GameServer.Groups.Group.t()) :: any()

after_lobby_create(t)

@callback after_lobby_create(GameServer.Lobbies.Lobby.t()) :: any()

after_lobby_delete(t)

@callback after_lobby_delete(GameServer.Lobbies.Lobby.t()) :: any()

after_lobby_host_change(t, integer)

@callback after_lobby_host_change(GameServer.Lobbies.Lobby.t(), integer()) :: any()

after_lobby_join(t, t)

@callback after_lobby_join(GameServer.Accounts.User.t(), GameServer.Lobbies.Lobby.t()) ::
  any()

after_lobby_leave(t, t)

@callback after_lobby_leave(GameServer.Accounts.User.t(), GameServer.Lobbies.Lobby.t()) ::
  any()

after_lobby_update(t)

@callback after_lobby_update(GameServer.Lobbies.Lobby.t()) :: any()

after_party_create(t)

@callback after_party_create(GameServer.Parties.Party.t()) :: any()

after_party_disband(t)

@callback after_party_disband(GameServer.Parties.Party.t()) :: any()

after_party_join(t, t)

@callback after_party_join(GameServer.Accounts.User.t(), GameServer.Parties.Party.t()) ::
  any()

after_party_kick(t, t, t)

@callback after_party_kick(
  GameServer.Accounts.User.t(),
  GameServer.Accounts.User.t(),
  GameServer.Parties.Party.t()
) :: any()

after_party_leave(t, integer)

@callback after_party_leave(GameServer.Accounts.User.t(), integer()) :: any()

after_party_update(t)

@callback after_party_update(GameServer.Parties.Party.t()) :: any()

after_startup()

@callback after_startup() :: any()

after_user_kicked(t, t, t)

after_user_login(t)

@callback after_user_login(GameServer.Accounts.User.t()) :: any()

after_user_offline(t)

@callback after_user_offline(GameServer.Accounts.User.t()) :: any()

after_user_online(t)

@callback after_user_online(GameServer.Accounts.User.t()) :: any()

after_user_register(t)

@callback after_user_register(GameServer.Accounts.User.t()) :: any()

after_user_updated(t)

@callback after_user_updated(GameServer.Accounts.User.t()) :: any()

before_chat_message(t, map)

@callback before_chat_message(GameServer.Accounts.User.t(), map()) :: hook_result(map())

before_group_create(t, map)

@callback before_group_create(GameServer.Accounts.User.t(), map()) :: hook_result(map())

before_group_join(t, t, map)

@callback before_group_join(
  GameServer.Accounts.User.t(),
  GameServer.Groups.Group.t(),
  map()
) ::
  hook_result(
    {GameServer.Accounts.User.t(), GameServer.Groups.Group.t(), map()}
  )

before_group_update(t, map)

@callback before_group_update(GameServer.Groups.Group.t(), map()) :: hook_result(map())

before_kv_get(t, kv_opts)

@callback before_kv_get(String.t(), kv_opts()) :: hook_result(:public | :private)

Called before a KV get/2 is performed. Implementations should return :public if the key may be read publicly, or :private to restrict access.

Receives the key and an opts map/keyword (see kv_opts/0). Return either the bare atom (e.g. :public) or {:ok, :public}; return {:error, reason} to block the read.

before_lobby_create(map)

@callback before_lobby_create(map()) :: hook_result(map())

before_lobby_delete(t)

@callback before_lobby_delete(GameServer.Lobbies.Lobby.t()) ::
  hook_result(GameServer.Lobbies.Lobby.t())

before_lobby_join(t, t, keyword)

before_lobby_leave(t, t)

before_lobby_update(t, map)

@callback before_lobby_update(GameServer.Lobbies.Lobby.t(), map()) :: hook_result(map())

before_party_create(t, map)

@callback before_party_create(GameServer.Accounts.User.t(), map()) :: hook_result(map())

before_party_update(t, map)

@callback before_party_update(GameServer.Parties.Party.t(), map()) :: hook_result(map())

before_stop()

@callback before_stop() :: any()

before_user_kicked(t, t, t)

before_user_update(t, map)

@callback before_user_update(GameServer.Accounts.User.t(), map()) :: hook_result(map())

on_custom_hook(t, list)

@callback on_custom_hook(String.t(), list()) :: any()

Handle a dynamically-exported RPC function.

This callback is used for function names that were registered at runtime (eg. via a plugin's after_startup/0 return value) and therefore may not exist as exported Elixir functions on the hooks module.

Receives the function name and the argument list.

Functions

call(name, args \\ [], opts \\ [])

Call an arbitrary function exported by the configured hooks module.

This is a safe wrapper that checks function existence, enforces an allow-list if configured and runs the call inside a short Task with a configurable timeout to avoid long-running user code.

Returns {:ok, result} | {:error, reason}

caller()

@spec caller() :: any() | nil

When a hooks function is executed via call/3 or internal_call/3, an optional :caller can be provided in the options. The caller will be injected into the spawned task's process dictionary and is accessible via GameServer.Hooks.caller/0 (the raw value) or caller_id/0 (the numeric id when the value is a user struct or map containing :id).

caller_id()

@spec caller_id() :: integer() | nil

caller_user()

@spec caller_user() :: GameServer.Accounts.User.t() | nil

Return the user struct for the current caller when available. This will attempt to resolve the caller via GameServer.Accounts.get_user!/1 when the caller is an integer id or a map containing an :id key. Returns nil when no caller or user is found.

exported_functions(mod \\ module())

Return a list of exported functions on the currently registered hooks module.

The result is a list of maps like: [%{name: "start_game", arities: [2,3]}, ...] This is useful for tooling and admin UI to display what RPCs are available.

internal_call(name, args \\ [], opts \\ [])

Call an internal lifecycle callback. When a callback is missing this returns a sensible default (eg. {:ok, attrs} for before callbacks) so domain code doesn't need to handle missing hooks specially in most cases.

internal_hooks()

Returns the set of internal lifecycle hook names that are not callable through the public RPC interface.

invoke(name, args \\ [])

Invoke a dynamic hook function by name.

This is used by GameServer.Schedule to call scheduled job callbacks. Unlike internal_call/3, this is designed for user-defined functions that are not part of the core lifecycle callbacks.

Returns :ok on success, {:error, reason} on failure or if the function doesn't exist.

module()

Return the configured module that implements the hooks behaviour.