Complete reference for the SpawnWeaver Godot SDK (v1.0.0). The entire client API lives on the SpawnWeaver autoload; every request method is awaitable and returns an SWResult. Guides: Rooms, State sync, Events, Storage.

The SpawnWeaver autoload

Properties

Property Type Meaning
status Status enum OFFLINE, CONNECTING, or ONLINE
player SWPlayer Your identity; null until start() succeeds
room SWRoom Your current room, maintained automatically; null when not in a room
is_host bool (read-only) true when you host your current room
latency_ms float Heartbeat round-trip in milliseconds
identity_scope String Advanced: isolates identity persistence per client instance (tools/tests). Leave empty in games

Constants

Constant Value Meaning
SDK_VERSION "1.0.0" Reported to the server and in debug reports
CONFIG_PATH res://spawnweaver.cfg Project config written by the editor dock
OVERRIDE_CONFIG_PATH user://spawnweaver_override.cfg Per-machine override (local tooling; never ship)
IDENTITY_PATH user://spawnweaver/identity.cfg Where the player token persists between runs
DEFAULT_SERVER_URL wss://spawnweaver.dev/connect Fallback server URL

Lifecycle

configure(project_key: String, server_url: String = "") -> void

Sets credentials in code instead of using res://spawnweaver.cfg. Call before start(). server_url is optional (defaults to production).

SpawnWeaver.configure("pk_your_key", "ws://127.0.0.1:5159/connect")

start() -> SWResult

Connects using the configured project key. Resolves once the session is welcomed (value: your SWPlayer) or the first connection attempt fails. After a successful start, the SDK auto-reconnects forever until stop(). Calling start() while already online resolves immediately; while connecting, it awaits the in-flight attempt.

var result := await SpawnWeaver.start()
if not result.ok:
    show_error(result.error.message)   # e.g. code "not-configured", "connect-failed"

stop() -> void

Disconnects and stops auto-reconnect. In-flight requests fail with code stopped; room_left("left") fires if you were in a room. Safe to call at any time.

is_online() -> bool

true when status == Status.ONLINE.

set_display_name(display_name: String) -> void

Sets the name other players see. Applied on your next create/join/match.

set_debug_enabled(enabled: bool) -> void

Toggles SDK console logging of every message and error.


Rooms

`create_room(options: Dictionary =

Creates a room and puts you in it as host. Value: the SWRoom (result.room).

Option Type Default Notes
name String none Display name
visibility String "private" "private" (code-only) or "public" (listable)
max_players int unlimited In-grace members count toward the cap
metadata Dictionary {} Your data, visible to joiners and listings
var result := await SpawnWeaver.create_room({"visibility": "public", "max_players": 4})
if result.ok:
    print(result.room.code)

Errors: already-in-room, not-connected.

join_room(code: String) -> SWResult

Joins by share code, or by a public room's id from list_rooms() (strings starting with room_/match_ are treated as ids). Value: the SWRoom.

var result := await SpawnWeaver.join_room("4V8772")

Errors: room-not-found, room-full, already-in-room, not-connected.

leave_room() -> SWResult

Leaves your current room. Value: null. Fires room_left("left") on success. Errors: not-in-room, not-connected.

list_rooms(limit: int = 20) -> SWResult

Lists joinable public rooms of your project, most recently active first. Value: Array of SWRoomSummary (result.rooms).

var result := await SpawnWeaver.list_rooms()
for summary in result.rooms:
    print(summary.code, " ", summary.player_count)

update_room(changes: Dictionary) -> SWResult

Host only. Updates room attributes; only provided fields change, but metadata replaces the whole map. Accepts the same keys as create_room options. Value: the updated SWRoom. All members receive room_updated.

Errors: not-host, not-in-room.


Matchmaking

`find_match(mode: String = "default", options: Dictionary =

Queues for a match and resolves when one is found (value: the SWRoom), the server times the ticket out, or you cancel. Options: region (String, default "global"), size (int, default 2, range 2–64). A second call replaces the first ticket.

var result := await SpawnWeaver.find_match("duel", {"region": "eu"})
if result.ok:
    start_game(result.room)
elif result.error.code == "match-timeout":
    offer_retry()

Errors: match-timeout (retryable), cancelled, already-in-room, invalid-payload (bad size, or | in mode/region), not-connected. Client-side await ceiling: 120 s.

cancel_matchmaking() -> SWResult

Cancels matchmaking: any awaited find_match() resolves with code cancelled, and the server-side ticket is removed. Value: {"hadTicket": bool}.


Events

`send_event(event_name: String, data: Dictionary =

Fire-and-forget relay to the other members of your room (you never receive your own event). Not awaitable; failures arrive on the error signal. Queued automatically while reconnecting (up to 128 messages) and flushed on resume; dropped with a warning when fully offline.

SpawnWeaver.send_event("fired", {"dir": [0, 1]})

State sync

All four calls resolve with value: the confirmed change payload (a raw Dictionary — e.g. {roomId, entityId, ownerId, patch, state}); the matching signal (room_state_changed / entity_changed / entity_removed) fires for everyone, including you. Patches are shallow merges; a null value removes the key.

set_room_state(patch: Dictionary) -> SWResult

Host only: patches the room-level shared state.

await SpawnWeaver.set_room_state({"phase": "combat", "old_key": null})

Errors: not-host, not-in-room, state-too-large, rate-limited.

set_entity(entity_id: String, data: Dictionary) -> SWResult

Creates or replaces an entity you own. Errors: not-in-room, state-forbidden (someone else owns it), state-limit-exceeded, state-too-large, rate-limited.

patch_entity(entity_id: String, patch: Dictionary) -> SWResult

Patches an entity you own. Errors: as set_entity, plus entity-not-found.

delete_entity(entity_id: String) -> SWResult

Deletes an entity you own. Everyone receives entity_removed(id, "deleted"). Errors: entity-not-found, state-forbidden, not-in-room.


Player storage

storage_get(key: String) -> SWResult

Value: the stored Variant, or null when the key was never set.

storage_set(key: String, value: Variant) -> SWResult

Persists any JSON-serializable Variant. Survives restarts. Value: null. Errors: storage-invalid-key, storage-value-too-large, storage-quota-exceeded.

storage_delete(key: String) -> SWResult

Value: booltrue when the key existed.

storage_list(prefix: String = "") -> SWResult

Value: Array[String] of this player's keys, optionally filtered by prefix.

await SpawnWeaver.storage_set("save", {"level": 3})
var save := await SpawnWeaver.storage_get("save")        # save.value == {"level": 3}
var keys := await SpawnWeaver.storage_list()             # keys.value == ["save"]
var gone := await SpawnWeaver.storage_delete("save")     # gone.value == true

Leaderboards

Project-scoped ranked score boards (guide). A board springs into existence on first submit; a player holds at most one entry per board. Board names match ^[a-z0-9_.\-]{1,64}$.

leaderboard_submit(board: String, score: int, mode: String = "max") -> SWResult

Submits a score. mode: "max" (default — keep the higher score), "min" (keep the lower; ranks read ascending), "latest" (always overwrite). Value: {best, rank, updated}updated is false when the submit didn't beat your stored score.

var result := await SpawnWeaver.leaderboard_submit("highscore", 4200)
if result.ok:
    print("rank #", result.value["rank"])

Errors: leaderboard-invalid-board, leaderboard-invalid-score, not-connected.

leaderboard_top(board: String, limit: int = 10) -> SWResult

The board's best entries, best first. limit 1–100. Value: Array[SWLeaderboardEntry]. Errors: leaderboard-invalid-board, not-connected.

leaderboard_around(board: String, entry_range: int = 5) -> SWResult

Your entry ± entry_range neighbors (1–50). Value: {entries: Array[SWLeaderboardEntry], player_rank: int, total: int}player_rank is -1 when you have no entry (the top of the board is returned instead). Errors: leaderboard-invalid-board, not-connected.


Diagnostics

get_ping_ms() -> float

The heartbeat round-trip latency (same as latency_ms).

create_debug_report() -> Dictionary

A diagnostic snapshot: SDK/engine versions, status, server URL, player/room ids, latency, reconnect attempts, and ring buffers of the last 50 messages and 10 errors.

create_debug_report_string() -> String

The report as pretty-printed JSON — paste into the dashboard's Debug Bundle viewer.

simulate_connection_loss() -> void

Testing helper: drops the socket so auto-reconnect and room resume run exactly as after a real network blip.


Signals

Broadcasts only — request outcomes come back via await, never signals.

Signal Arguments When
connected Session welcomed; requests will flow (also after each reconnect)
disconnected err: SWError The connection dropped; auto-reconnect follows unless stop() was called
reconnecting attempt: int, delay: float A reconnect attempt will run after delay seconds
status_changed new_status: Status status changed
room_joined room: SWRoom You entered a room — create, join, matchmaking, or automatic resume
room_left reason: String You left: "left", "disconnected", or "expired"
player_joined player: SWPlayer Someone joined your room (never echoes you)
player_left player: SWPlayer, reason: String Someone left: "left" or "disconnected" (grace expired)
player_disconnected player: SWPlayer A member's connection blipped; they may return within the grace window
player_reconnected player: SWPlayer That member returned
host_changed player: SWPlayer Host migration promoted a new host
room_updated room: SWRoom Room attributes changed (name/visibility/max_players/metadata)
event_received event_name: String, data: Dictionary, sender: SWPlayer Another player sent an event
room_state_changed state: Dictionary, patch: Dictionary Room state changed (includes your own confirmed changes)
entity_changed entity_id: String, state: Dictionary, patch: Dictionary, owner_id: String An entity changed (includes your own confirmed changes)
entity_removed entity_id: String, reason: String An entity was removed: "deleted" or "owner-left"
error err: SWError An unsolicited server error (awaited calls carry their errors in the SWResult)

Models

SWResult

The outcome of every awaited call.

Member Type Meaning
ok bool true on success
value Variant The success value (call-specific; see each method above)
error SWError The failure, or null on success
room SWRoom (accessor) value when it is a room (create/join/find_match/update), else null
rooms Array (accessor) value when it is a list (list_rooms), else []
player SWPlayer (accessor) value when it is a player (start), else null

SWError

Member Type Meaning
code String Machine-readable kebab-case code (see Error codes)
message String Human-readable explanation, safe to log
retryable bool true when the same call may succeed after a short backoff
details Dictionary Optional extra context (e.g. {"entityId": "p1"})

SWPlayer

Member Type Meaning
id String Stable player id (survives reconnects and restarts)
name String Display name given at create/join (may be empty)
connected bool false while inside the disconnect grace window
is_local bool true when the entry is you
display_name() String UI-ready name: name, or a short id fallback

SWRoom

Member Type Meaning
id String Server id (room_… / match_…)
code String Short join code (e.g. "4V8772")
visibility String "public" or "private"
name String Optional display name
max_players int Player cap; 0 = unlimited
metadata Dictionary Developer metadata
host_id String Current host's player id (kept valid by host migration)
players Array[SWPlayer] The roster, maintained automatically
state Dictionary Room-level shared state (host-writable)
entities Dictionary entity_id -> {"owner_id": String, "state": Dictionary}
get_player(player_id) SWPlayer Roster lookup, or null
host() SWPlayer The host's roster entry, or null
other_players() Array[SWPlayer] Everyone except you

SWLeaderboardEntry

One row from leaderboard_top() / leaderboard_around().

Member Type Meaning
rank int 1-based rank; ties share a rank
player_id String The entry owner's stable player id
player_name String Display name sent with the submit (may be empty)
score int The stored score
updated_at String ISO-8601 UTC timestamp of the last accepted submit
is_local bool true when the entry is you
display_name() String UI-ready name: player_name, or a short id fallback

SWRoomSummary

One entry from list_rooms().

Member Type Meaning
id String Room id (can be passed to join_room())
code String Join code
name String Optional display name
player_count int Current members
max_players int Cap; 0 = unlimited
metadata Dictionary Developer metadata
has_space() bool true when the room can still be joined

Nodes

LobbyBrowser

A complete lobby UI with zero code (extends Control): player name field, Create room, Quick match, join by code, and a live public-room list. Built from standard Controls, so your project Theme restyles it. Connect entered_room and switch to your game scene — that's the whole integration.

Export Type Default Meaning
auto_start bool true Call SpawnWeaver.start() when entering the tree
show_name_field bool true Show the player-name field (persisted per device)
show_quick_match bool true Show the Quick match button
quick_match_size int 2 Players per quick match (2–64)
default_max_players int 8 Cap for rooms created here (0 = unlimited)
hide_on_join bool true Hide on room entry; reappear when the room is left
refresh_seconds float 3.0 Public-room list refresh interval
title String "Multiplayer" Heading text (empty hides it)
Signal Arguments When
entered_room room: SWRoom A room was entered (create, join, code, or match)
errored err: SWError An action failed (also shown in the status line)

SpawnSync

Drop-in network sync for a Node2D or Node3D — add as a child of the node to replicate. Local copies send (dirty-checked, at send_rate); remote copies interpolate. Re-registers after reconnects/room changes; deletes its entity on exit.

Export Type Default Meaning
entity_id String "" Stable id; empty on a local node = your player id
is_local bool false true on the single copy this client owns
sync_position bool true Replicate position
sync_rotation bool true Replicate rotation
sync_scale bool false Replicate scale
synced_properties PackedStringArray [] Extra parent properties (primitives only)
send_rate float 8.0 Local updates per second (1–10)
interpolate bool true Smooth remote copies toward incoming state
interpolation_speed float 16.0 Higher = snappier (1–40, frame-rate independent)
free_parent_on_remove bool true Free the parent when the entity is removed server-side

Method: set_local(local: bool) — switches the controlling client and (re)registers (called by PlayerSpawner when spawning).

State keys sent: 2D — x, y, rot, sx, sy; 3D — x, y, z, rx, ry, rz, sx, sy, sz; plus each synced_properties name verbatim.

PlayerSpawner

Spawns one instance of your player scene per player in the room; despawns on leave; survives reconnects (idempotent reconcile); auto-wires any SpawnSync inside the scene (entity id + local authority) before it enters the tree.

Export Type Default Meaning
player_scene PackedScene The scene instantiated per player (required)
spawn_root NodePath empty Where instances are added; empty = the spawner's parent
spawn_points NodePath empty Node whose children are used as spawn positions, round-robin by roster order
spawn_local_player bool true Also spawn an avatar for you
Signal Arguments When
player_spawned player: SWPlayer, node: Node After a player's avatar entered the tree
player_despawned player_id: String, node: Node After a player's avatar was removed

Optional hook on the player scene's root script, called right before add_child:

func setup(player: SWPlayer, is_local: bool) -> void:
    pass

Error codes

SDK-local codes (raised without a server round-trip)

Code Retryable When
not-configured no start() with no project key configured
connect-failed no The first connection attempt failed (start() resolves with this; no auto-retry)
connection-lost yes A mid-session drop (carried by the disconnected signal)
stopped no stop() was called; in-flight requests fail with this
not-connected no A request made while offline — call start() first
timeout yes No reply within 10 s (120 s for find_match)
disconnected yes The connection dropped mid-request
cancelled no cancel_matchmaking() resolved a pending find_match()
match-timeout yes The server found no match within its window

Server codes

Code Retryable Meaning
malformed-message no Frame wasn't valid JSON or had no type
unknown-message-type no No handler for the type (SDK/server version mismatch)
invalid-payload no A required field is missing or invalid
room-not-found no No such room code, or the room expired
room-full no The room is at max_players
already-in-room no Already in a room — leave it first
not-in-room no The operation needs a current room
not-host no Host-only operation (update_room, room state)
payload-too-large no Message over the 16 KB limit
rate-limited yes Sending too fast — back off and retry
entity-not-found no No such entity in this room
state-forbidden no Only the entity's owner may change it
state-limit-exceeded no Entity count cap (50) reached
state-too-large no Entity (4 KB) or room state (16 KB) cap exceeded
leaderboard-invalid-board no Board name doesn't match ^[a-z0-9_.\-]{1,64}$
leaderboard-invalid-score no Score missing or outside ±2^53-1
storage-invalid-key no Storage key empty or over 128 chars
storage-value-too-large no Stored value over 64 KB
storage-quota-exceeded no At the 100-key quota

Limits and remedies: Limits. Wire-level details: Protocol reference.