# SDK setup

Get the Wavedash SDK running in your game

Source: https://docs.wavedash.com/sdk/setup

The Wavedash SDK gives your game access to player accounts, leaderboards, achievements, multiplayer, cloud saves, and more. This page walks you through getting it set up.

## How the SDK works

When your game runs on Wavedash, the platform makes `Wavedash` available in the browser before your code starts. During local development, `wavedash dev` does the same thing with a sandbox version so you can test everything offline.

Methods that talk to servers are asynchronous. In JavaScript they return Promises; in the native bindings they resolve via `await`, coroutines, or engine-specific response events. Methods that read local state (like the current player) are synchronous.

<GithubLink href="https://github.com/wvdsh/sdk-js" label="View the SDK on GitHub" />

### Typed access in JavaScript/TypeScript

Optionally install `@wvdsh/sdk-js` and import `Wavedash` directly in your game script for editor autocomplete and type safety:

```bash
npm install @wvdsh/sdk-js
```

```typescript
import Wavedash from "@wvdsh/sdk-js";

Wavedash.init({ debug: true });
Wavedash.on(Wavedash.Events.LOBBY_JOINED, (payload) => {
  console.log(`Joined lobby ${payload.lobbyId}`);
});
```

The import gives you the host's injected SDK instance with full TypeScript types — methods, events, and payloads all autocomplete. Importing from anywhere in your codebase returns the same SDK singleton.

## Initialization

Call `Wavedash.init(config?)` once — before any other SDK method — to apply your settings and mark the game as loaded. This is what reveals your game from behind the loading screen, so it's required for every game, even one that uses no other SDK features. `init()` is synchronous and returns a boolean (`true` on first call, `false` if already initialized). You do not need to `await` it.

`init()` also calls `loadComplete()` internally, so a single `init()` is enough for most games. See [Load lifecycle](#load-lifecycle) below if you want to report progress and signal load completion separately.

<CodeGroup>
```gdscript Godot
func _ready():
    WavedashSDK.backend_connected.connect(_on_connected)
    WavedashSDK.init({"debug": true, "deferEvents": true})
    WavedashSDK.ready_for_events()

func _on_connected(payload):
    print("SDK is ready!")
    var username = WavedashSDK.get_username()
    print("Playing as: ", username)
```

```csharp Unity
using System.Collections.Generic;

void Awake()
{
    Wavedash.SDK.Init(new Dictionary<string, object>
    {
        { "debug", true },
        { "deferEvents", true }
    });
    Wavedash.SDK.ReadyForEvents();
}

void Start()
{
    var user = Wavedash.SDK.GetUser();
    if (user != null)
        Debug.Log($"Playing as: {user["username"]}");
}
```

```lua Defold
function init(self)
    wavedash.init({ debug = true, deferEvents = true }, function(_, event, payload)
        if event == wavedash.EVENT_BACKEND_CONNECTED then
            print("SDK is ready!")
            local user = wavedash.get_user()
            if user then
                print("Playing as:", user.username)
            end
        end
    end)

    wavedash.ready_for_events()
end
```

```javascript JavaScript
Wavedash.init({ debug: true, deferEvents: true });
Wavedash.readyForEvents();

const user = Wavedash.getUser();
console.log(`Playing as: ${user.username}`);
```
</CodeGroup>

<Note>
The current Defold binding initializes with `wavedash.init(callback)` and does not expose JS-style `debug` / `deferEvents` init options.
</Note>

## Load lifecycle

<Note>
In **Godot** and **Unity**, the generated `index.html` that embeds your game reports load progress and completion automatically — the engine's own loader drives the Wavedash loading bar before your game code runs. Those bindings don't expose the methods below and you don't need to call them; `init()` is enough. Manual load reporting applies to **JavaScript** and **Defold**.
</Note>

Wavedash tracks two separate signals while your game boots:

1. **Load progress** — a 0-to-1 value the shell uses to render its loading bar. Call `updateLoadProgressZeroToOne(progress)` while your game is downloading assets or initializing.
2. **Load complete** — fires once, when the player can actually interact. `init()` calls this for you, or you can call `loadComplete()` directly if you want to signal completion before you're ready to initialize the full SDK (e.g., hand-off from a splash screen).

<CodeGroup>
```javascript JavaScript
// Report progress while assets download
Wavedash.updateLoadProgressZeroToOne(0.2);
await downloadAssets();
Wavedash.updateLoadProgressZeroToOne(0.8);
await startRuntime();

// Either of these works — init() calls loadComplete() internally
Wavedash.init({ debug: false });
// Wavedash.loadComplete(); // only needed if you call it before init()
```

```lua Defold
-- Report progress while assets download
wavedash.update_load_progress_zero_to_one(0.2)
-- download assets...
wavedash.update_load_progress_zero_to_one(0.8)

-- init() marks the game as loaded; call load_complete() only if you
-- need to signal completion before init()
wavedash.init(function() end)
-- wavedash.load_complete()
```
</CodeGroup>

Both methods are idempotent — calling them twice is a no-op.

### Ready-state getters

<Note>
These getters exist only in the JavaScript SDK (`@wvdsh/sdk-js`). The Godot, Unity, and Defold bindings don't expose them.
</Note>

The JavaScript SDK exposes three boolean getters for defensive code that needs to know where you are in the lifecycle:

| Getter | True when |
|--------|-----------|
| `initialized` | `init()` has been called at least once |
| `eventsReady` | The deferred event queue has been flushed (either by `init()` or a manual `readyForEvents()`) |
| `gameLoaded` | `loadComplete()` has fired |

```javascript
if (!Wavedash.initialized) Wavedash.init();
if (Wavedash.eventsReady && Wavedash.gameLoaded) {
  // SDK is fully booted and serving events
}
```

## Loading third-party scripts

`Wavedash.loadScript(src)` is a small helper that appends a `<script>` tag to the page and resolves once it finishes loading. Useful for lazy-loading analytics, engine runtimes, or any script you don't want to block initial page load.

```javascript
await Wavedash.loadScript("https://cdn.example.com/analytics.js");
// analytics global is now available
```

## Overlay

Call `toggleOverlay()` to show or hide the Wavedash in-game overlay (friends list, invites, settings). Wire it to a pause button or a keyboard shortcut so players can reach Wavedash's built-in UI without leaving your game.

<CodeGroup>
```gdscript Godot
WavedashSDK.toggle_overlay()
```

```csharp Unity
Wavedash.SDK.ToggleOverlay();
```

```lua Defold
wavedash.toggle_overlay()
```

```javascript JavaScript
Wavedash.toggleOverlay();
```
</CodeGroup>

## Response format

Most async SDK methods that talk to Wavedash services return a response object. Check `success` before using `data`.

<CodeGroup>
```javascript
const response = await Wavedash.getLeaderboard("high-scores");

if (response.success) {
  console.log(`Leaderboard: ${response.data.name}`);
} else {
  console.error(`Error: ${response.message}`);
}
```

```lua Defold
-- Defold can work with async SDK methods in two ways:

-- 1) Using a coroutine. Calling an async function from within a coroutine will
-- provide the SDK method response as the return value of the function call.
local co = coroutine.create(function()
    local response = wavedash.get_leaderboard("high-scores");
    if response.success then
        print("Leaderboard:", response.data.name)
    else
        print("Error:", response.message)
    end
end)
coroutine.resume(co)


-- 2) Using an event listener. Calling an async function from the main thread
-- will provide the SDK method response as an event to the event listener set
-- when initialising Wavedash.
function init()
    wavedash.init({}, function(_, event, payload)
        if event == hash("getLeaderboard") then
            if payload.success then
                print("Leaderboard:", payload.data.name)
            else
                print("Error:", payload.message)
            end
        end
    end)
    wavedash.get_leaderboard("high-scores")
end
```
</CodeGroup>

When a call fails, `message` explains what went wrong.

## Error handling

Wrap async service calls in try/catch and check `success`:

<CodeGroup>
```gdscript Godot
func _on_leaderboard(response):
    if not response.get("success", false):
        push_error(response.get("message", "leaderboard error"))
        return
    var board = response["data"]
```

```csharp Unity
try
{
    var board = await Wavedash.SDK.GetLeaderboard("high-scores");
    if (board == null)
    {
        Debug.LogWarning("Leaderboard call failed");
        return;
    }
}
catch (Exception ex)
{
    Debug.LogException(ex);
}
```

```javascript JavaScript
try {
  const response = await Wavedash.getLeaderboard("high-scores");
  if (!response.success) {
    console.error(response.message);
    return;
  }
  useBoard(response.data);
} catch (e) {
  console.error(e);
}
```
</CodeGroup>

## Configuration options

Pass these to `init()` when you need non-default behavior:

| Option | What it does |
|--------|-------------|
| `debug` | Turns on verbose logging in the browser console |
| `deferEvents` | Queues lobby and multiplayer events until you call `readyForEvents()` |
| `p2p` | Custom P2P networking settings (see [Types reference](/sdk/types)) |

<Tip>
If you set `deferEvents: true`, make sure you call `readyForEvents()` once your game is done loading. Otherwise queued events will never reach your handlers.
</Tip>

## Launch params

When a player opens your game through a URL with `wvdsh_`-prefixed query parameters, Wavedash strips the prefix and passes them to your game as key-value pairs. Call `getLaunchParams()` after `init()` to read them.

The most common use case is **lobby invite links** — when a player clicks an invite link, the `lobby` param contains the lobby ID so your game can join automatically.

<CodeGroup>
```gdscript Godot
WavedashSDK.init({})
var params = WavedashSDK.get_launch_params()
if params.has("lobby"):
    WavedashSDK.join_lobby(params["lobby"])
```

```csharp Unity
Wavedash.SDK.Init(new Dictionary<string, object>());
var parameters = Wavedash.SDK.GetLaunchParams();
if (parameters.TryGetValue("lobby", out var lobbyId))
    await Wavedash.SDK.JoinLobby(lobbyId);
```

```lua Defold
function init(self)
    wavedash.init({}, function() end)
    local params = wavedash.get_launch_params()
    if params and params.lobby then
        wavedash.join_lobby_async(params.lobby)
    end
end
```

```javascript JavaScript
Wavedash.init();
const params = Wavedash.getLaunchParams();
if (params.lobby) {
  await Wavedash.joinLobby(params.lobby);
}
```
</CodeGroup>

### How it works

1. A player visits a URL like `https://wavedash.com/games/my-game?wvdsh_lobby=abc123`
2. Wavedash strips the `wvdsh_` prefix from matching query parameters
3. The resulting key-value pairs (`{ lobby: "abc123" }`) are passed to your game
4. Your game reads them with `getLaunchParams()` and acts on them

<Note>
Only query parameters prefixed with `wvdsh_` are forwarded to your game. Other query parameters are ignored.
</Note>

### Built-in params

| Param | Description |
|-------|-------------|
| `lobby` | Lobby ID — the player followed an invite link. Join with `joinLobby()`. See [Multiplayer lobbies](/multiplayer/lobbies). |

You can also pass custom data by adding `wvdsh_`-prefixed query parameters to any game URL. For example, `?wvdsh_mode=ranked` would appear as `{ mode: "ranked" }` in `getLaunchParams()`.

## Connection events

Listen for connection changes to show online/offline status in your UI:

<CodeGroup>
```gdscript Godot
func _ready():
    WavedashSDK.backend_connected.connect(_on_connected)
    WavedashSDK.backend_disconnected.connect(_on_disconnected)
    WavedashSDK.backend_reconnecting.connect(_on_reconnecting)

func _on_connected(payload):
    print("Connected (attempt %d)" % payload["connectionCount"])

func _on_disconnected(payload):
    print("Disconnected (was ever connected: %s)" % payload["hasEverConnected"])

func _on_reconnecting(payload):
    print("Reconnecting (retry %d)" % payload["connectionRetries"])
```

```csharp Unity
void Awake()
{
    Wavedash.SDK.OnBackendConnected += data =>
        Debug.Log($"Connected (attempt {data["connectionCount"]})");
    Wavedash.SDK.OnBackendDisconnected += data =>
        Debug.Log($"Disconnected (was ever connected: {data["hasEverConnected"]})");
    Wavedash.SDK.OnBackendReconnecting += data =>
        Debug.Log($"Reconnecting (retry {data["connectionRetries"]})");
}
```

```lua Defold
function init(self)
    wavedash.init({}, function(_, event, payload)
        if event == wavedash.EVENT_BACKEND_CONNECTED then
            print(("Connected (attempt %d)"):format(payload.connectionCount))
        elseif event == wavedash.EVENT_BACKEND_DISCONNECTED then
            print(("Disconnected (was ever connected: %s)"):format(tostring(payload.hasEverConnected)))
        elseif event == wavedash.EVENT_BACKEND_RECONNECTING then
            print(("Reconnecting (retry %d)"):format(payload.connectionRetries))
        end
    end)
end
```

```javascript JavaScript
Wavedash.on(Wavedash.Events.BACKEND_CONNECTED, (payload) => {
  console.log(`Connected (attempt ${payload.connectionCount})`);
});
Wavedash.on(Wavedash.Events.BACKEND_DISCONNECTED, (payload) => {
  console.log(`Disconnected (was ever connected: ${payload.hasEverConnected})`);
});
Wavedash.on(Wavedash.Events.BACKEND_RECONNECTING, (payload) => {
  console.log(`Reconnecting (retry ${payload.connectionRetries})`);
});
```
</CodeGroup>

## What's next

<CardGroup cols="2">
  <Card title="Player identity" href="/sdk/players">
    Accounts, avatars, friends, and presence.
  </Card>
  <Card title="Multiplayer" href="/multiplayer/lobbies">
    Lobbies, chat, and P2P networking.
  </Card>
  <Card title="Achievements" href="/sdk/achievements">
    Reward players for hitting milestones.
  </Card>
  <Card title="Engine guides" href="/engines">
    Godot, Unity, Three.js, and more.
  </Card>
</CardGroup>

See the [Events reference](/sdk/events) for the complete event list.
