API Reference

Complete reference for every public member of the Board Godot SDK. Each module is a section; each section lists properties, methods, signals, and constants as applicable, with GDScript signatures, parameter tables, return values, and usage examples.

The SDK ships as a single autoload named Board. All modules hang off that autoload — for example, Board.input.subscribe() and Board.save.await_create(...). Off-device (editor, desktop, simulator), Board.is_on_device is false and every module either no-ops or returns a typed default (empty arrays, null for the typed objects, false, request-id sentinels).

Warning: Always guard SDK calls with if not Board.is_on_device: return at the top of any function that depends on the native plugin. Off-device defaults are documented per-method below — but adding explicit guards makes intent clear and keeps editor previews clean.

Capability Check

A tiny surface lives directly on the Board autoload for runtime feature-detection. Use Board.is_on_device to short-circuit every other SDK call when the plugin isn’t loaded (editor, desktop builds, simulators).

Member Type Description
Board.is_on_device bool true when the BoardSDK Android plugin singleton is registered with the engine. false everywhere else.
Board.initialize(app_id: String) void Initialize the SDK with your game’s app ID. Must be called once before any session/save/avatar/pause call.
func _ready() -> void:
    if not Board.is_on_device:
        return
    Board.initialize("00000000-0000-0000-0000-000000000000")

Note: There’s no SDK-exposed version number to feature-gate against. The native AAR ships with the GDScript SDK as a single package, so a game always knows the exact SDK version it’s bundled with. Any skew against the OS services is handled inside the native plugin itself — per-service capability checks with graceful degradation — so games never branch on a “bridge API version”.

Note: The SDK doesn’t expose a set_log_level helper, and there’s no settings asset or SDK-side log-level type to configure. Godot’s native logger already handles verbosity via print_verbose() and OS.is_stdout_verbose() — use those. Runtime tuning that the SDK does expose (smoothing, persistence, tracking) is passed directly to Board.input.activate(...); see that method below.


Board

The top-level autoload. Holds module accessors, lifecycle helpers, and a couple of device-detection properties.

Source: addon/addons/board_sdk/board.gd.

Properties

Property Type Description
is_on_device bool true when the BoardSDK Android plugin singleton is registered with the engine — i.e., the app is running on Board hardware with the AAR loaded. false in the editor, on desktop builds, in simulators, and anywhere else the native plugin isn’t present.
input BoardInputModule Touch and Piece input accessor. See Board.input.
ui_input BoardUiInputModule Finger-to-UI input bridge accessor. See Board.ui_input.
session BoardSessionModule Players and profiles accessor. See Board.session.
save BoardSaveModule Save game CRUD accessor. See Board.save.
avatar BoardAvatarModule Avatar loader accessor. See Board.avatar.
pause BoardPauseModule Pause overlay accessor. See Board.pause.
application BoardApplicationModule App-lifecycle accessor. See Board.application.

Methods

initialize(app_id: String) -> void

Initialize the plugin with this game’s app ID. Must be called once before any session/save/avatar/pause call. Subsequent calls are ignored.

Parameter Type Description
app_id String Your game’s app ID. Format: UUID string (e.g., "00000000-0000-0000-0000-000000000000").

Returns: void.

Off-device behavior: returns immediately without calling the plugin.

Example:

func _ready() -> void:
    if not Board.is_on_device:
        return
    Board.initialize("00000000-0000-0000-0000-000000000000")

Board.input

Touch and Piece input. Fires a single signal on every inference frame with the current contact set. There are no separate edge-detection signals — to get discrete edges (tap-down/up via is_touched, or piece-lifted), keep your own previous-frame map keyed by contact_id and diff it each frame. See the diff pattern below.

Source: addon/addons/board_sdk/board_input.gd.

Contact Dictionary Shape

Every contact emitted in contacts_received or returned from get_current_contacts() is a Dictionary with these keys:

Key Type Description
contact_id int Stable across frames for the same physical contact. Use it to track a specific Piece instance from one frame to the next.
x float Display-pixel X. Origin top-left.
y float Display-pixel Y, Y-down. Origin top-left.
orientation float Degrees, screen-CW positive. Feeding into Vector2(cos θ, sin θ) yields a unit vector pointing in the same direction the physical Piece is facing. Only meaningful for Glyphs.
type_id int BoardContactType: 0 finger, 1 glyph, 2 blob. On the device Godot channel every contact is stamped type_id = 1, so this field does not distinguish a finger from a Piece. Use glyph_id for that (see the note below).
phase_id int BoardContactPhase: 0 NONE, 1 BEGAN, 2 MOVED, 3 ENDED, 4 CANCELED, 5 STATIONARY.
glyph_id int 0 for finger; 1+ for a Piece type. The numeric identity of the Piece — your game maps these to game-specific names. Filter/dispatch on this field to tell fingers from Pieces.
is_touched bool true while the Piece is being physically held by a hand.
frame_number int Kernel frame number for cross-layer correlation.

Note: To tell a finger from a Piece, filter on glyph_id (0 is a finger, 1+ is a Piece type), not type_id. On the device Godot channel the tracker stamps every contact with type_id = 1 (glyph), so type_id can’t discriminate finger from Piece here even though the TYPE_FINGER / TYPE_GLYPH / TYPE_BLOB constants exist. glyph_id is the reliable discriminator (0 = finger, 1+ = Piece). To follow one specific Piece instance across frames, key off contact_id, which stays stable for the lifetime of that contact.

Note: contacts_received carries raw Dictionary entries by design — the ~60 fps hot path stays untyped to avoid a per-frame allocation per contact. When you want typed access to a single contact (outside the per-frame loop — e.g. when reacting to a tap or inspecting one Piece), wrap it with BoardContact.from_dict(c). The resulting BoardContact exposes typed fields plus the BoardContact.Type / BoardContact.Phase enums, a facing() -> Vector2 helper, and is_piece(). Don’t convert the whole per-frame snapshot; wrap individual contacts on demand.

Properties

Property Type Description
is_subscribed bool Whether the subscription is currently active.

Methods

activate(model_asset, position_smoothing?, rotation_smoothing?, persistence?, fast_tracking?) -> bool

Load a Piece Set Model .tflite and start the touch detector. Required once after Board.initialize() before any contacts will fire. The optional tracker parameters are how you tune runtime input behavior — there’s no separate settings asset.

func activate(model_asset: String,
              position_smoothing: float = 0.035,
              rotation_smoothing: float = 0.004,
              persistence: int = 4,
              fast_tracking: bool = true) -> bool
Parameter Type Description
model_asset String Model filename inside the APK’s assets/, e.g. "models/arcade_v1.3.7.tflite". Game devs ship their own .tflite alongside the build — the SDK doesn’t bundle one because each Piece Set determines which model file is correct.
position_smoothing float EMA constant on the position output. Default 0.035. Lower = more smoothing, higher = more responsive.
rotation_smoothing float EMA constant on the rotation output. Default 0.004.
persistence int Frames a contact persists after detection drops out before being declared ended. Default 4.
fast_tracking bool Aggressive tracking heuristic. Default true. Set to false for the most stable per-frame readings at the cost of some responsiveness.

Returns: true if both load and activation succeeded; false otherwise.

Off-device behavior: returns false without calling the plugin.

Example:

Board.input.activate("models/arcade_v1.3.7.tflite")
# Or with tuned tracker parameters for a tight feel:
Board.input.activate("models/my_model.tflite", 0.02, 0.002, 6, true)

subscribe() -> void

Begin the touch subscription. Idempotent. Requires a prior activate() to actually deliver contacts — subscribe() only opens the data stream; activate() starts the touch detector.

Returns: void.

Off-device behavior: returns immediately.

unsubscribe() -> void

Stop the touch subscription.

Returns: void.

get_current_contacts() -> Array

Snapshot of currently-active contacts. No subscription required.

Returns: Array[Dictionary] of contacts in the same shape as contacts_received.

Off-device behavior: returns [].

Signals

contacts_received(contacts: Array)

Fires every inference frame (~60 fps once subscribed) with the current Array[Dictionary] of contacts. Stationary contacts persist across frames — a Piece you place and walk away from stays in every snapshot until lifted.

Use this for per-frame visualization or anywhere you need the full contact set.

Example:

Board.input.contacts_received.connect(func(contacts: Array) -> void:
    for c in contacts:
        if c.glyph_id != 0:  # 0 == finger, 1+ == a Piece type
            print("Piece glyph=%d at (%s, %s)" % [c.glyph_id, c.x, c.y])
)

Note: The loop above reads the raw Dictionary directly — that’s the intended hot-path pattern. For typed access to an individual contact (e.g. once you’ve isolated the one you care about), wrap it with BoardContact.from_dict(c).

Detecting edges with a per-frame diff

contacts_received is a per-frame snapshot — it doesn’t tell you what changed. To turn it into discrete edges (tap-down / tap-up via is_touched, or a Piece being lifted off the board), keep your own map of the previous frame’s contacts keyed by contact_id and diff it on each frame. This is the same approach the Unity SDK uses.

Example:

var _prev: Dictionary = {}  # contact_id -> contact Dictionary from the last frame

func _ready() -> void:
    Board.input.contacts_received.connect(_on_contacts)

func _on_contacts(contacts: Array) -> void:
    var current: Dictionary = {}
    for c in contacts:
        current[c.contact_id] = c
        var prev = _prev.get(c.contact_id)
        if prev == null:
            pass  # new contact this frame
        elif c.is_touched and not prev.is_touched:
            # Rising edge — the user just pressed down on this Piece
            _fire_weapon_at(c.glyph_id)
        elif not c.is_touched and prev.is_touched:
            # Falling edge — the user released this Piece
            pass

    # Anything in _prev but not in current was lifted / ended this frame.
    for old_id in _prev:
        if not current.has(old_id):
            print("Piece %d removed" % _prev[old_id].glyph_id)

    _prev = current

Constants

const TYPE_FINGER := 0
const TYPE_GLYPH := 1
const TYPE_BLOB := 2

BoardContactType mirror. Compare against contact.type_id (which can be 0, 1, or 2).

const PHASE_NONE := 0
const PHASE_BEGAN := 1
const PHASE_MOVED := 2
const PHASE_ENDED := 3
const PHASE_CANCELED := 4
const PHASE_STATIONARY := 5

BoardContactPhase mirror. Compare against contact.phase_id.

Note: These TYPE_* / PHASE_* int constants still exist (same values) for reading a raw contact Dictionary’s type_id / phase_id straight from contacts_received. The typed BoardContact wrapper exposes the same values as the BoardContact.Type and BoardContact.Phase enums.


BoardContact

Typed wrapper for a single contact. The per-frame contacts_received snapshot stays as raw Dictionary entries to avoid allocating one wrapper per contact at ~60 fps; construct a BoardContact on demand with BoardContact.from_dict(c) when you want typed field access for an individual contact outside the hot path.

Construction

BoardContact.from_dict(contact: Dictionary) -> BoardContact

Wrap a raw contact Dictionary (as emitted by contacts_received or returned from get_current_contacts()) in a typed BoardContact.

Parameter Type Description
contact Dictionary A raw contact entry in the Contact Dictionary Shape.

Returns: a BoardContact.

Fields

Field Type Description
contact_id int Stable across frames for the same physical contact.
position Vector2 Display-pixel position, origin top-left, Y-down. Built from the raw x / y keys (Vector2(d.x, d.y)); the typed wrapper has no separate x / y fields.
orientation float Degrees, screen-CW positive. Only meaningful for Glyphs.
type BoardContact.Type The contact type. See the Type enum below.
phase BoardContact.Phase The contact phase. See the Phase enum below.
glyph_id int 0 for finger; 1+ for a Piece type.
is_touched bool true while the Piece is being physically held by a hand.
frame_number int Kernel frame number for cross-layer correlation.

Helpers

facing() -> Vector2

Unit vector pointing in the direction the physical Piece is facing, derived from orientation. Only meaningful for Glyphs.

Returns: Vector2.

is_piece() -> bool

Whether this contact is a Piece (i.e., glyph_id != 0) rather than a finger.

Returns: bool.

Enums

enum Type { FINGER, GLYPH, BLOB }
enum Phase { NONE, BEGAN, MOVED, ENDED, CANCELED, STATIONARY }

BoardContact.Type and BoardContact.Phase are the typed equivalents of the raw Board.input.TYPE_* / PHASE_* int constants used when reading a raw contact Dictionary.

Example:

# Outside the per-frame loop — wrap one contact for typed access.
var contact := BoardContact.from_dict(c)
if contact.is_piece() and contact.phase == BoardContact.Phase.BEGAN:
    var aim := contact.facing()
    _spawn_at(contact.position, aim)

Board.ui_input

Finger-to-UI input bridge. On Board hardware the SDK swallows all raw Android motion events, because the touch controller reports a Piece’s Glyph as ordinary capacitive touch points that are indistinguishable from a finger at the MotionEvent layer, so a Piece placed on a Button would otherwise press it. This module re-injects only the finger contacts (those with glyph_id == 0) from the classified Board pipeline as synthetic InputEventScreenTouch / InputEventScreenDrag events, so Control / Button / _gui_input UI responds to fingers exactly as it would to normal touches, but never to Pieces, which are filtered out here. On by default on device.

It rides the same touch stream the game uses: contacts only flow after Board.input.activate(model) + Board.input.subscribe(). Before a model is active there are no contacts, so on-device UI receives no input. Activate the detector early if you show menus before gameplay.

Source: addon/addons/board_sdk/board_ui_input.gd.

Properties

Property Type Description
is_enabled bool Read-only. Whether finger-to-UI synthesis is currently active. true by default on device.

Methods

set_enabled(enabled: bool) -> void

Enable or disable finger-to-UI synthesis. On by default on device. Disabling releases all held synthetic touches so the UI doesn’t get stuck in a pressed state. Use this to opt out if your game drives all of its UI from Board.input.contacts_received directly and doesn’t want synthetic events reaching Control nodes.

Parameter Type Description
enabled bool true to synthesize finger touches into UI input; false to stop (and release any held synthetic touches).

Returns: void.

Example:

# Drive all UI from contacts_received yourself; turn off the synthetic bridge.
Board.ui_input.set_enabled(false)

Board.session

Session and player management. Lists active players, presents the OS player selector, and manages the active profile.

Source: addon/addons/board_sdk/board_session.gd.

BoardPlayer

Every player returned from get_players() / get_active_profile() is a typed BoardPlayer with these fields:

Field Type Description
player_id String Persistent app-specific player ID. Stable across sessions for the same profile.
session_id int Session-unique ID. Pass to present_replace_player() to target a specific player within the current session.
display_name String Display name.
type BoardPlayer.Type Either PROFILE or GUEST. See the Type enum below.
avatar_id String Avatar identifier. Pass to Board.avatar.await_load_avatar(int(avatar_id)) to get the decoded texture.

Helpers

Helper Returns Description
is_guest() bool true when type == BoardPlayer.Type.GUEST.
is_profile() bool true when type == BoardPlayer.Type.PROFILE.

Enum

enum Type { PROFILE, GUEST }

Methods

get_players() -> Array[BoardPlayer]

All players in the current session.

Returns: Array[BoardPlayer] of players in the shape described above.

Off-device behavior: returns [].

get_player_count() -> int

Number of players currently in the session.

Returns: int.

Off-device behavior: returns 0.

Note: The session roster is owned by the OS. A game can’t add or remove players directly — instead it asks the OS to present its selector overlay via present_add_player() / present_replace_player(), or it clears the roster back to the active profile with reset_players(). The user makes the actual add/remove/replace choice in that overlay, and the new roster lands via players_changed and a fresh get_players().

reset_players() -> bool

Reset the session to just the active profile (drops all guests and replaced players).

Returns: true on success, false otherwise.

Off-device behavior: returns false.

is_session_ready() -> bool

Whether the session manager has finished its initial profile load.

Returns: bool.

Off-device behavior: returns false.

are_services_ready() -> bool

Whether the OS-level services are connected.

Returns: bool.

Off-device behavior: returns false.

get_active_profile() -> BoardPlayer

The system-wide active profile.

Returns: a BoardPlayer, or null if no profile is active.

Off-device behavior: returns null.

Example:

var profile := Board.session.get_active_profile()
if profile == null:
    push_warning("no active profile")
else:
    print("active profile: %s" % profile.display_name)

present_add_player(ai_type_indices := PackedInt32Array()) -> int

Show the OS player selector to add a new player.

Parameter Type Description
ai_type_indices PackedInt32Array Optional. Restricts which AI player types the selector offers, by index into the array registered with set_ai_player_types(). Leave empty (the default) to offer all registered types.

Returns: request ID (int). Await player_selector_finished (or player_selector_completed / player_selector_failed separately) for the result. Returns -1 if the plugin isn’t available.

Example:

var rid := Board.session.present_add_player()
if rid >= 0:
    var result = await Board.session.player_selector_finished
    var players := Board.session.get_players()

# Or restrict the selector to a subset of registered AI types (indices 0 and 2):
Board.session.present_add_player(PackedInt32Array([0, 2]))

present_replace_player(session_id: int, ai_type_indices := PackedInt32Array()) -> int

Show the OS player selector to replace a specific existing player.

Parameter Type Description
session_id int The session-unique ID of the player to be replaced.
ai_type_indices PackedInt32Array Optional. Restricts which AI player types the selector offers, by index into the array registered with set_ai_player_types(). Leave empty (the default) to offer all registered types.

Returns: request ID (int). Same await pattern as present_add_player(). Returns -1 if the plugin isn’t available.

show_profile_switcher() -> void / hide_profile_switcher() -> void

Show or hide the profile-switcher overlay.

Returns: void.

set_ai_player_types(types: Array) -> void

Register AI player types this game supports. Used by the OS player selector to populate the “Add AI” UI.

Parameter Type Description
types Array Array of Dictionary entries, each with name (short label) and description (one-line explanation).

Returns: void.

Example:

Board.session.set_ai_player_types([
    { "name": "Easy", "description": "Plays defensively" },
    { "name": "Hard", "description": "Plans 3 turns ahead" },
])

Pass an empty array to clear the registration. Order is preserved in the UI.

Signals

player_selector_completed(request_id: int)

Fires after the OS player-selector overlay dismisses successfully (a player was picked or removed). request_id matches the value returned from present_add_player() / present_replace_player(). Emission is deferred to the next idle frame so callers awaiting immediately after the request don’t miss a signal that the Java side fires synchronously.

player_selector_failed(request_id: int, reason: String)

Fires when the player-selector overlay can’t open or the user dismisses without picking. reason is "dismissed" for user-cancel.

player_selector_finished(request_id: int, ok: bool)

Fires whenever the player-selector overlay settles, regardless of outcome. ok is true if a player was picked, false on dismiss or open failure. Convenience signal for callers that don’t need to distinguish success from dismissal — removes the need to race two signals manually.

players_changed

Fires whenever the active player list mutates (a player added, removed, or replaced via the OS selector overlay, profile selection, reset_players(), etc.). No payload — re-query get_players() after to read the new state.

Constants

const TYPE_PROFILE := "profile"
const TYPE_GUEST := "guest"

Type-string constants. A BoardPlayer’s type field is the typed BoardPlayer.Type enum (PROFILE / GUEST) — prefer player.is_profile() / player.is_guest() over comparing these strings.


Board.save

Save game CRUD. All async methods return a request ID immediately and emit a typed signal carrying that ID with the payload. await_* helpers wrap the pattern with await for code that prefers an inline style.

Source: addon/addons/board_sdk/board_save.gd.

Warning: The load method is named load_datanot load (which shadows a GDScript built-in that returns a Resource and produces parse errors). There is no direct delete (matching the Unity SDK): a save is removed by detaching its players with remove_players_from_save / remove_active_profile_from_save, and the OS deletes it once none remain.

BoardSaveMetadata

The metadata carried by save_created, the entries in the save_listed array, and the value returned from await_create() are all typed BoardSaveMetadata objects with these fields:

Field Type Description
id String Stable identifier for this save. Pass to load_data(), update(), remove_players_from_save(), etc.
description String User-visible description.
created_at int Unix epoch milliseconds.
updated_at int Unix epoch milliseconds.
played_time int Cumulative play time in milliseconds.
file_size int Size of the stored data payload in bytes.
game_version String Game version string at save time.
player_count int Number of players associated with this save.

Helpers

Helper Returns Description
is_valid() bool true when this metadata refers to a real save (i.e., it has a non-empty id).

Methods

create(description, data, played_time_ms, game_version, cover_png?) -> int

Create a new save game.

func create(description: String,
            data: PackedByteArray,
            played_time_ms: int,
            game_version: String,
            cover_png := PackedByteArray()) -> int
Parameter Type Description
description String User-visible description. Bound by get_max_description_length().
data PackedByteArray Game state bytes. Bound by get_max_data_size().
played_time_ms int Cumulative play time at save time.
game_version String Game version string.
cover_png PackedByteArray Optional. PNG bytes stored as the save’s cover image. This is the cover-write path; read it back later via load_cover_image() / await_load_cover_image(). Defaults to empty (no cover).

Returns: request ID (int). Result lands on save_created(rid, metadata) or save_failed(rid, error).

Off-device behavior: returns -1.

load_data(save_id: String) -> int

Load the byte payload of a save by its ID.

Parameter Type Description
save_id String The save’s id.

Returns: request ID (int). Result lands on save_loaded(rid, PackedByteArray) or save_failed(rid, error).

list() -> int

List all saves for this game (for the active profile).

Returns: request ID (int). Result lands on save_listed(rid, Array) (an Array[BoardSaveMetadata]) or save_failed(rid, error).

update(save_id, description, data, played_time_ms, game_version, cover_png?) -> int

Overwrite an existing save with new payload + metadata.

func update(save_id: String,
            description: String,
            data: PackedByteArray,
            played_time_ms: int,
            game_version: String,
            cover_png := PackedByteArray()) -> int

The optional trailing cover_png (PackedByteArray, default empty) is the cover-write path; pass PNG bytes to overwrite the save’s cover image, then read it back via load_cover_image() / await_load_cover_image().

Returns: request ID (int). Result lands on save_updated(rid) or save_failed(rid, error).

remove_players_from_save(save_id: String) -> int

Detach this session’s players from the save. There is no direct delete (matching the Unity SDK); the OS deletes the save once no players remain associated with it.

Returns: request ID (int). Result lands on save_players_removed(rid) or save_failed(rid, error).

remove_active_profile_from_save(save_id: String) -> int

Disassociate just the active profile from this save (other players retain access).

Returns: request ID (int). Result lands on save_active_profile_removed(rid) or save_failed(rid, error).

load_cover_image(save_id: String) -> int

Request the cover image (PNG bytes) for a save.

Returns: request ID (int). Result lands on save_cover_image_loaded(rid, PackedByteArray) or save_failed(rid, error).

get_max_data_size() -> int

Per-save byte limit reported by the OS.

Returns: int bytes.

Off-device behavior: returns 0.

get_max_app_storage_size() -> int

Total app storage quota across all saves.

Returns: int bytes.

Off-device behavior: returns 0.

get_max_description_length() -> int

Maximum length of the description field.

Returns: int characters.

Off-device behavior: returns 0.

get_app_storage_info() -> Dictionary

Current app storage usage for this game. Synchronous — no request ID, no signal.

Returns: Dictionary with these keys:

Key Type Description
total_storage int Total storage quota for this app, in bytes.
used_storage int Bytes currently consumed by this app’s saves.
remaining_storage int Bytes still available, in bytes.
usage_percentage float Fraction of the quota used, 0.01.0.

Off-device behavior: returns {}.

Example:

var info := Board.save.get_app_storage_info()
if not info.is_empty():
    print("Using %d of %d bytes (%.0f%%)" % [
        info.used_storage, info.total_storage, info.usage_percentage * 100.0])

await_* Helpers

These wrap the fire-and-forget + signal pattern with await. They poll process_frame until either the matching success signal or save_failed fires (because GDScript’s await doesn’t natively support racing two distinct signals). On failure, they push_error and return a typed empty value.

await_list() -> Array[BoardSaveMetadata]

Equivalent to list() + waiting for save_listed / save_failed.

Returns: Array[BoardSaveMetadata] of save metadata, or [] on failure.

Example:

var saves := await Board.save.await_list()
for s in saves:
    print("%s%s" % [s.id, s.description])

await_load(save_id: String) -> PackedByteArray

Equivalent to load_data(save_id) + waiting for save_loaded / save_failed.

Returns: PackedByteArray of save bytes, or empty PackedByteArray() on failure.

await_create(description, data, played_time_ms, game_version, cover_png?) -> BoardSaveMetadata

Equivalent to create(...) + waiting for save_created / save_failed. The optional trailing cover_png := PackedByteArray() is the cover-write path; pass PNG bytes to store the save’s cover image.

Returns: a BoardSaveMetadata, or null on failure.

Example:

var blob := PackedByteArray()
blob.resize(64)
var meta := await Board.save.await_create("Save 1", blob, 0, "1.0.0")
if meta == null:
    push_warning("save failed")

await_update(save_id, description, data, played_time_ms, game_version, cover_png?) -> bool

Equivalent to update(...) + waiting for save_updated / save_failed. The optional trailing cover_png := PackedByteArray() is the cover-write path; pass PNG bytes to overwrite the save’s cover image.

func await_update(save_id: String,
                  description: String,
                  data: PackedByteArray,
                  played_time_ms: int,
                  game_version: String,
                  cover_png := PackedByteArray()) -> bool
Parameter Type Description
save_id String The save’s id.
description String User-visible description. Bound by get_max_description_length().
data PackedByteArray Game state bytes. Bound by get_max_data_size().
played_time_ms int Cumulative play time at save time.
game_version String Game version string.
cover_png PackedByteArray Optional. PNG bytes stored as the save’s cover image. Read it back later via load_cover_image() / await_load_cover_image(). Defaults to empty (cover unchanged).

Returns: true on success, false on failure.

await_load_cover_image(save_id: String) -> PackedByteArray

Equivalent to load_cover_image(save_id) + waiting for save_cover_image_loaded / save_failed.

Parameter Type Description
save_id String The save’s id.

Returns: PackedByteArray of the cover PNG bytes, or empty PackedByteArray() on failure.

await_remove_players(save_id: String) -> bool

Equivalent to remove_players_from_save(save_id) + waiting for save_players_removed / save_failed. Detaches this session’s players from the save; the OS deletes the save once no players remain associated with it.

Parameter Type Description
save_id String The save’s id.

Returns: true on success, false on failure.

await_remove_active_profile(save_id: String) -> bool

Equivalent to remove_active_profile_from_save(save_id) + waiting for save_active_profile_removed / save_failed. Disassociates just the active profile from this save (other players retain access).

Parameter Type Description
save_id String The save’s id.

Returns: true on success, false on failure.

Signals

save_created(request_id: int, metadata: BoardSaveMetadata)

Fires when create() succeeds. metadata is a BoardSaveMetadata.

save_loaded(request_id: int, data: PackedByteArray)

Fires when load_data() succeeds. data is the byte payload.

save_listed(request_id: int, saves: Array)

Fires when list() succeeds. saves is an Array[BoardSaveMetadata].

save_updated(request_id: int)

Fires when update() succeeds. No payload.

save_players_removed(request_id: int)

Fires when remove_players_from_save() succeeds. No payload.

save_active_profile_removed(request_id: int)

Fires when remove_active_profile_from_save() succeeds. No payload.

save_cover_image_loaded(request_id: int, png_bytes: PackedByteArray)

Fires when load_cover_image() succeeds. png_bytes is the PNG byte stream.

save_failed(request_id: int, error: String)

Fires when any save operation fails. request_id matches the failed call’s returned id. error is a short error string.


Board.avatar

Avatar loader. Loads a player’s avatar as a ready-to-display ImageTexture. Results are cached, and concurrent loads of the same id coalesce into a single request, so it’s cheap to call once per frame from UI code. Avatars are scoped to players — pass the avatar_id from a BoardPlayer returned by Board.session.get_players() (mirrors the Unity SDK’s BoardPlayer.avatar).

Source: addon/addons/board_sdk/board_avatar.gd.

Note: The avatar_id field on a BoardPlayer is a String, so call these methods with int(player.avatar_id). The returned ImageTexture is already decoded — assign it straight to a TextureRect or Sprite2D; there’s no PNG-decoding step to wire up.

Methods

await_load_avatar(avatar_id: int) -> ImageTexture

Load and decode the avatar for the given id, returning a ready-to-display texture. Cached: repeated calls for the same id return the cached texture, and concurrent loads of the same id coalesce into a single underlying request.

Parameter Type Description
avatar_id int A player’s avatar id. Read it from a BoardPlayer’s avatar_id field via int(player.avatar_id).

Returns: an ImageTexture, or null on failure.

Off-device behavior: returns null.

Example:

for player in Board.session.get_players():
    var tex := await Board.avatar.await_load_avatar(int(player.avatar_id))
    if tex != null:
        $TextureRect.texture = tex

await_default_avatar() -> ImageTexture

Load and decode the default avatar (id 0). Same caching behavior as await_load_avatar().

Returns: an ImageTexture, or null on failure.

Off-device behavior: returns null.

Example:

var tex := await Board.avatar.await_default_avatar()
if tex != null:
    $TextureRect.texture = tex

clear_cache() -> void

Drop every cached avatar texture. The next await_load_avatar() / await_default_avatar() call re-fetches from the OS.

Returns: void.


Board.pause

System pause overlay. Configure with set_context(); the user’s choice on dismissal lands on pause_result_received.

Source: addon/addons/board_sdk/board_pause.gd.

Context Schema

The Dictionary passed to set_context(). All keys are optional — omitted keys preserve native-side defaults.

Key Type Description
game_id String Your game’s identifier. Used by BoardOS to disambiguate context across apps. Omit (don’t pass empty string) if unknown — empty gameId is rejected on the native side and silently disables the pause button.
game_name String Display name shown in the pause overlay header.
offer_save_option bool If true, the OS offers a “Save & Quit” button alongside the standard “Quit”.
custom_buttons Array[Dictionary] Array of custom buttons, each with id (String), title (String), and icon (one of the Board.pause.ICON_* constants).
audio_tracks Array[Dictionary] Array of audio sliders, each with id (String), name (String), and value (int 0..100).

Methods

set_context(context: Dictionary) -> void

Configure the system pause overlay.

Parameter Type Description
context Dictionary See Context Schema.

Returns: void.

Warning: Don’t serialize empty defaults. set_context() only forwards keys that are present in context. If you set "game_id": "", the empty string overrides the native default and the pause button silently no-ops. Omit absent keys entirely rather than sending empty values.

Example:

Board.pause.set_context({
    "game_name": "My Game",
    "offer_save_option": true,
    "custom_buttons": [{
        "id": "restart",
        "title": "Restart",
        "icon": Board.pause.ICON_CIRCULAR_ARROW,
    }],
    "audio_tracks": [
        { "id": "music", "name": "Music", "value": 75 },
        { "id": "sfx",   "name": "SFX",   "value": 90 },
    ],
})

update_audio_tracks(tracks: Array) -> void

Update only the audio tracks while preserving everything else from the last set_context() call. Useful for live volume sliders without re-sending the whole config every keystroke.

Parameter Type Description
tracks Array Same Array shape as set_context()’s audio_tracks.

Returns: void.

Example:

# User adjusted music volume to 60
Board.pause.update_audio_tracks([
    { "id": "music", "name": "Music", "value": 60 },
    { "id": "sfx",   "name": "SFX",   "value": 90 },
])

clear_context() -> void

Clear the pause context entirely. The pause overlay falls back to its native defaults.

Returns: void.

poll_result() -> BoardPauseResult

Polling fallback for the pause_result_received signal. Returns a BoardPauseResult whose is_present() is false if no pause action has been committed yet.

Returns: a BoardPauseResult (same type as the signal’s result argument). When nothing has been committed, is_present() returns false.

Note: Prefer the signal. poll_result() consumes the same one-shot result the signal would deliver, so calling both in the same app causes one to miss the event. This helper exists for code that can’t easily wire a signal handler.

Signals

pause_result_received(result: BoardPauseResult)

Fires when the user dismisses the pause overlay with an action. result is a BoardPauseResult, whose fields are:

Field Type Description
action String One of the ACTION_* constants.
custom_button_id String Present only when action == ACTION_CUSTOM_BUTTON. The id of the button the user tapped.
audio_tracks Array Present only when audio tracks were set and the user adjusted one or more sliders. Same shape as the input audio_tracks.

Example:

Board.pause.pause_result_received.connect(func(result: BoardPauseResult):
    match result.action:
        Board.pause.ACTION_RESUME:
            pass  # user resumed
        Board.pause.ACTION_QUIT:
            Board.application.quit()
        Board.pause.ACTION_SAVE_AND_QUIT:
            _save_then_quit()
        Board.pause.ACTION_CUSTOM_BUTTON:
            if result.custom_button_id == "restart":
                get_tree().reload_current_scene.call_deferred()
)

Constants

const ACTION_RESUME           := "RESUME"
const ACTION_QUIT             := "EXIT_GAME_UNSAVED"
const ACTION_SAVE_AND_QUIT    := "EXIT_GAME_SAVED"
const ACTION_CUSTOM_BUTTON    := "CUSTOM_ACTION"

Action constants emitted in result.action. Always match against the constants, not the literal strings, in case future SDK releases change the values.

const ICON_NONE           := ""
const ICON_CIRCULAR_ARROW := "circulararrow"
const ICON_DOOR_WITH_ARROW := "doorwitharrow"
const ICON_LEFT_ARROW     := "leftarrow"
const ICON_SQUARE         := "square"

Built-in icon names accepted by custom_buttons[].icon.


BoardPauseResult

Typed result delivered by pause_result_received and returned from poll_result(). Captures the choice the user made when dismissing the system pause overlay.

Note: This is the typed output of the pause overlay. The input you hand to set_context() (and update_audio_tracks()) — including the custom_buttons and audio_tracks entry dictionaries — stays a caller-supplied Dictionary; see Context Schema.

Fields

Field Type Description
action String One of the Board.pause.ACTION_* constants.
custom_button_id String Present only when action == Board.pause.ACTION_CUSTOM_BUTTON. The id of the button the user tapped.
audio_tracks Array Present only when audio tracks were set and the user adjusted one or more sliders. Same shape as the input audio_tracks.

Helpers

is_present() -> bool

Whether a pause action has actually been committed. poll_result() returns a BoardPauseResult whose is_present() is false when there’s nothing to consume yet.

Returns: bool.

Example:

# Polling style — only act once a result is actually present.
var result := Board.pause.poll_result()
if result.is_present() and result.action == Board.pause.ACTION_QUIT:
    Board.application.quit()

Board.application

Application-lifecycle module: app-wide concerns that don’t belong to any of the input/session/save/avatar/pause domains. Covers clean app termination.

Source: addon/addons/board_sdk/board_application.gd.

Note: Godot apps handle pause/resume/focus-loss via NOTIFICATION_APPLICATION_* on any Node — those don’t need to route through Board.

Note: The system menu button (top-right corner) is owned by the OS — the plugin shows it automatically on init/resume and hides it on pause/stop/destroy. A game can’t toggle it. To control what that button opens, configure the pause overlay with Board.pause.set_context(...); without a context the button has nothing to show.

Methods

quit() -> void

Terminate the calling app cleanly. Equivalent to the user swiping the app away in Recent Apps. Fire-and-forget; the process is gone before this returns.

Returns: void.

Off-device behavior: falls through to get_tree().quit() so editor / desktop builds can also exercise this code path.

Warning: Use Board.application.quit() on-device, not get_tree().quit(). The SDK version notifies BoardOS so the user lands cleanly back in the launcher; raw get_tree().quit() skips that notification and the launcher behaves as if the app crashed.

Example:

func _on_quit_pressed() -> void:
    Board.application.quit()

Cross-Reference: Async Methods and Result Signals

Quick lookup for the request-id → result-signal pattern used across Board.save. All result signals carry the matching request_id as their first argument; all failure signals carry (request_id, error: String).

Method Success signal Failure signal
Board.save.create(...) save_created(rid, BoardSaveMetadata) save_failed(rid, error)
Board.save.load_data(id) save_loaded(rid, PackedByteArray) save_failed(rid, error)
Board.save.list() save_listed(rid, Array[BoardSaveMetadata]) save_failed(rid, error)
Board.save.update(...) save_updated(rid) save_failed(rid, error)
Board.save.remove_players_from_save(id) save_players_removed(rid) save_failed(rid, error)
Board.save.remove_active_profile_from_save(id) save_active_profile_removed(rid) save_failed(rid, error)
Board.save.load_cover_image(id) save_cover_image_loaded(rid, PackedByteArray) save_failed(rid, error)
Board.session.present_add_player() player_selector_completed(rid) and player_selector_finished(rid, true) player_selector_failed(rid, reason) and player_selector_finished(rid, false)
Board.session.present_replace_player(sid) player_selector_completed(rid) and player_selector_finished(rid, true) player_selector_failed(rid, reason) and player_selector_finished(rid, false)

The corresponding await_* helpers:

Helper Wraps Returns on failure
Board.save.await_list() list() + save_listed / save_failed []
Board.save.await_load(id) load_data(id) + save_loaded / save_failed PackedByteArray()
Board.save.await_create(...) create(...) + save_created / save_failed null
Board.save.await_update(...) update(...) + save_updated / save_failed false
Board.save.await_load_cover_image(id) load_cover_image(id) + save_cover_image_loaded / save_failed PackedByteArray()
Board.save.await_remove_players(id) remove_players_from_save(id) + save_players_removed / save_failed false
Board.save.await_remove_active_profile(id) remove_active_profile_from_save(id) + save_active_profile_removed / save_failed false
Board.avatar.await_load_avatar(id) cached avatar load + decode null
Board.avatar.await_default_avatar() cached default-avatar load + decode null