cdylib or staticlib, include include/arcane_sdk.h, then init at launch — passing nothing but an error buffer.
The client is a process-wide singleton — you init once and read from getters, with no handle to carry through C# or Blueprint.
Error codes match Rust SdkError::code() — use Errors when debugging.
Header
include/arcane_sdk.h) and is attached to each GitHub Release. Regenerate after FFI changes (cbindgen 0.29.4):
Return conventions
Two conventions, by function kind. Both are in the header as named constants. Actions —arcane_sdk_init, arcane_sdk_refresh, arcane_sdk_set_graphics, arcane_sdk_achievement_unlock, arcane_sdk_lobby_invite, arcane_sdk_lobby_leave, arcane_sdk_lobby_close:
Getters — the
char *buf functions:
Actions
arcane_sdk_init
ARCANE_GAME_ID and the signed-in account in ARCANE_USER_ID on the game process, and the SDK reads both. Set them yourself to run your build outside the launcher — see Local development.
It takes no string argument, so it never returns 1: the result is 0 or 2. On 2, err_buf holds code: message — hint (context…). Parse up to the first : for the stable code — missing_game_id is what you get when nothing set ARCANE_GAME_ID.
Errors: every code in the Errors table except not_initialized.
arcane_sdk_refresh
1 if arcane_sdk_init has not succeeded. On failure the client keeps its previous state.
arcane_sdk_shutdown
arcane_sdk_frame
arcane_sdk_init succeeds, and cannot fail.
arcane_sdk_set_graphics
"2560x1440" and "high" — attached to the FPS samples that follow. Empty strings clear them. Returns 1 on a null / non-UTF-8 argument or before init. Takes a short lock, so never call it per frame.
arcane_sdk_achievement_unlock
key is the key from the Arcane portal (NUL-terminated UTF-8). Idempotent — call it every time the condition holds; an already-unlocked or queued (desktop app offline) answer returns 0.
One synchronous loopback call, so never call it from the render loop. Returns 1 on a null / non-UTF-8 key or before init, 2 on an SDK error written to err_buf — invalid_argument for a malformed key (keys are lowercase; raised before any call), unknown_achievement when the title does not define it. See Achievements.
arcane_sdk_achievement_is_unlocked
arcane_sdk_achievements_json filled — memory only, no call. Returns 1 (unlocked), 0 (locked), -1 (not initialised), -2 (null / non-UTF-8 key), or -4 when the list has never been loaded or did not carry that key.
arcane_sdk_lobby_invite, arcane_sdk_lobby_leave, arcane_sdk_lobby_close
1 on a null / non-UTF-8 id or before init, 2 on an SDK error written to err_buf: not_friends, lobby_not_found, lobby_closed, invalid_argument for a malformed id (raised before any call).
close ends the lobby for everyone; its members get a lobby_closed event. There is no host migration.
Getters
arcane_sdk_ownership returns ARCANE_OWNERSHIP_OWNED (0) or ARCANE_OWNERSHIP_DRM_DISABLED (1). Both mean launch the game.
arcane_sdk_last_error_json writes the last failure as JSON — {"code","message","hint","retryable","context"} — for engines that want to render full detail. It returns -4 when nothing has failed since the last success.
arcane_sdk_session_json writes the play session state:
tracking is "active", "pending" or "disabled"; lobby_events is "off", "active" or "unavailable" — see Lobbies.
arcane_sdk_achievements_json is the C form of the Rust list(): it makes one synchronous loopback call, writes every achievement of the title, and fills the cache arcane_sdk_achievement_is_unlocked reads. Call it on a loading or achievements screen, never per frame.
unlocked_at is a Unix timestamp, or null while locked. A -4 here means the call failed — read arcane_sdk_last_error_json for the code.
arcane_sdk_friends_json is the C form of the Rust friends().list(): one synchronous loopback call returning this player’s friends with their presence. Call it when a friends menu opens or on a timer of your own, never per frame.
in_game is true for a friend playing this title right now — the one named in ARCANE_GAME_ID. stale is true when the desktop app answered from its cache because it is offline; the list is still usable. A -4 means the call failed — read arcane_sdk_last_error_json for the code.
Lobbies
arcane_sdk_lobby_create, arcane_sdk_lobby_join_code, arcane_sdk_lobby_join and arcane_sdk_lobby_get each make one synchronous loopback call and write the same lobby object — arcane_sdk_lobby_get reads one without joining or leaving anything:
visibility is ARCANE_LOBBY_FRIENDS (0), ARCANE_LOBBY_CODE (1) or ARCANE_LOBBY_FRIENDS_AND_CODE (2). payload_b64 is your connection blob already base64-encoded, at most 4096 raw bytes — a null pointer means no payload. Payloads come back base64 too; Arcane never reads them. join_code is null for a friends-only lobby and for a member who is not the host.
A -2 means a bad argument — a payload that is not base64, an unknown visibility, a null or non-UTF-8 id. A -4 means the call failed: read arcane_sdk_last_error_json for lobby_not_found, lobby_full, lobby_closed, not_friends, or invalid_argument when the join code or payload was rejected before any call.
arcane_sdk_launch_join_code reads the launcher’s stashed code on the first call and caches it for the process; -4 when there is none, when the desktop app predates the route, or in offline-only mode.
arcane_sdk_lobby_events_json reads memory only — the arcane-session thread does the polling, armed by the first lobby call — and drains the queue only once the JSON is safely in your buffer. A -3 leaves every event queued, so retry with a bigger buffer and lose nothing. Event objects carry type first, then lobby_id, then the fields of that type: invite has join_code, from_user_id, pseudo; member_joined has user_id, pseudo, payload; member_left has user_id; lobby_closed has nothing more. A {"type":"resync"} event has no lobby_id at all: Arcane dropped events before this client fetched them, so re-read the lobbies you are in with arcane_sdk_lobby_get.
Buffer sizing: user ids and device hashes fit comfortably in 64 bytes; allow 256 for the session JSON, 512 for err_buf, 1024 for the error JSON, 8 for a join code, and size the achievement, friends and lobby JSON to your title (a few hundred bytes per entry, plus your payloads at 4/3 their raw size once base64-encoded). A short buffer is never a silent truncation — you get -3 and an empty string.
Example
Safety notes
- Pointers must be valid for the duration of the call
err_bufmay be null orerr_len == 0if you do not need the message- Getter buffers must point to at least
lenwritable bytes - Do not free strings owned by the SDK; only your own buffers are written into
- All functions are safe to call from any thread; the singleton is behind a lock
arcane_sdk_frametakes only a read lock and no allocation — it is the one function meant for the render loop