GameServer.Hooks behaviour (GameServer v1.0.509)

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.

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_lobby_create(term)

@callback after_lobby_create(term()) :: any()

after_lobby_delete(term)

@callback after_lobby_delete(term()) :: any()

after_lobby_host_change(term, term)

@callback after_lobby_host_change(term(), term()) :: any()

after_lobby_join(t, term)

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

after_lobby_leave(t, term)

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

after_lobby_update(term)

@callback after_lobby_update(term()) :: any()

after_startup()

@callback after_startup() :: any()

after_user_kicked(t, t, term)

@callback after_user_kicked(
  GameServer.Accounts.User.t(),
  GameServer.Accounts.User.t(),
  term()
) :: any()

after_user_login(t)

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

after_user_register(t)

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

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(term)

@callback before_lobby_delete(term()) :: hook_result(term())

before_lobby_join(t, term, term)

@callback before_lobby_join(GameServer.Accounts.User.t(), term(), term()) ::
  hook_result({GameServer.Accounts.User.t(), term(), term()})

before_lobby_leave(t, term)

@callback before_lobby_leave(GameServer.Accounts.User.t(), term()) ::
  hook_result({GameServer.Accounts.User.t(), term()})

before_lobby_update(term, map)

@callback before_lobby_update(term(), map()) :: hook_result(map())

before_stop()

@callback before_stop() :: any()

before_user_kicked(t, t, term)

@callback before_user_kicked(
  GameServer.Accounts.User.t(),
  GameServer.Accounts.User.t(),
  term()
) ::
  hook_result(
    {GameServer.Accounts.User.t(), GameServer.Accounts.User.t(), term()}
  )

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.

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.