# Multiplayer lobbies

Create and manage multiplayer game lobbies

Source: https://docs.wavedash.com/multiplayer/lobbies

The Lobbies API lets you create multiplayer rooms where players can gather before or during gameplay. Lobbies support real-time messaging, metadata storage, and automatic P2P connection setup.

## Listening for `LOBBY_JOINED`

Both `createLobby()` and `joinLobby()` put the current player into a lobby, and in both cases the SDK fires a `LOBBY_JOINED` event once membership is confirmed. This is your single source of truth for "I'm now in a lobby" — subscribe **before** you call create or join, then drive your UI from the event handler.

<CodeGroup>
```gdscript Godot
func _ready():
    WavedashSDK.lobby_joined.connect(_on_lobby_joined)

func _on_lobby_joined(payload):
    var lobby_id = payload["lobbyId"]
    var users = payload["users"]
    var host_id = payload["hostId"]
    print("Joined ", lobby_id, " with ", users.size(), " players")
```

```csharp Unity
void Awake()
{
    Wavedash.SDK.OnLobbyJoined += OnLobbyJoined;
}

void OnLobbyJoined(Dictionary<string, object> payload)
{
    string lobbyId = (string)payload["lobbyId"];
    var users = (List<object>)payload["users"];
    string hostId = (string)payload["hostId"];
    Debug.Log($"Joined {lobbyId} with {users.Count} players");
}
```

```javascript JavaScript
Wavedash.on(Wavedash.Events.LOBBY_JOINED, (payload) => {
  console.log(`Joined ${payload.lobbyId} with ${payload.users.length} players`);
});
```

```lua Defold
function init(self)
    wavedash.init({}, function(_, event, payload)
        if event == wavedash.EVENT_LOBBY_JOINED then
            print("Joined", payload.lobbyId, "with", #payload.users, "players")
        end
    end)
end
```
</CodeGroup>

<Warning>
**Creating a lobby already joins it.** Do not call `joinLobby()` with the ID returned by `createLobby()` — the player is in the lobby as soon as `LOBBY_JOINED` fires, and calling join again will error.
</Warning>

## Creating a lobby

Make sure your `LOBBY_JOINED` listener is wired up first (see above), then call create. The returned lobby ID is useful for invite links and logging, but you do not need to call `joinLobby()` with it.

<CodeGroup>
```gdscript Godot
func create_lobby():
    await WavedashSDK.create_lobby(WavedashConstants.LOBBY_TYPE_PUBLIC, 4)
    # lobby_joined signal fires once the player is in — handle your UI there
```

```csharp Unity
public async void CreateLobby()
{
    // OnLobbyJoined (wired up in Awake) fires once the player is in.
    await Wavedash.SDK.CreateLobby(
        WavedashConstants.LobbyVisibility.PUBLIC,
        maxPlayers: 4);
}
```

```javascript JavaScript
// LOBBY_JOINED fires once the player is in — handle your UI there.
await Wavedash.createLobby(Wavedash.LobbyVisibility.PUBLIC, 4);
```

```lua Defold
local function create_lobby()
    -- wavedash.EVENT_LOBBY_JOINED fires once the player is in — handle your UI there
    wavedash.create_lobby_async(wavedash.LOBBY_VISIBILITY_PUBLIC, 4)
end
```
</CodeGroup>

### Visibility options

| Value | Constant | Description |
|-------|----------|-------------|
| `0` | `PUBLIC` | Anyone can find and join |
| `1` | `FRIENDS_ONLY` | Only friends can see and join |
| `2` | `PRIVATE` | Only joinable with the lobby ID |

In JavaScript use `Wavedash.LobbyVisibility.PUBLIC` / `FRIENDS_ONLY` / `PRIVATE`. In Defold use `wavedash.LOBBY_VISIBILITY_PUBLIC` / `LOBBY_VISIBILITY_FRIENDS_ONLY` / `LOBBY_VISIBILITY_PRIVATE`.

## Joining and listing

Same rule as creating: set up the `LOBBY_JOINED` listener first, then call `joinLobby()`.

<CodeGroup>
```gdscript Godot
# Make sure lobby_joined is connected before calling join_lobby (see above).
var response = await WavedashSDK.list_available_lobbies()
if response.success and response.data.size() > 0:
    WavedashSDK.join_lobby(response.data[0].lobbyId)
```

```csharp Unity
// Make sure OnLobbyJoined is subscribed before calling JoinLobby (see above).
var lobbies = await Wavedash.SDK.ListAvailableLobbies();
if (lobbies != null && lobbies.Count > 0)
    await Wavedash.SDK.JoinLobby((string)lobbies[0]["lobbyId"]);
```

```lua Defold
-- Make sure wavedash.EVENT_LOBBY_JOINED is handled before calling join_lobby_async (see above).
local co = coroutine.create(function()
    local response = wavedash.list_available_lobbies_async()
    if response.success and #response.data > 0 then
        wavedash.join_lobby_async(response.data[1].lobbyId)
    end
end)
assert(coroutine.resume(co))
```

```javascript JavaScript
// Make sure LOBBY_JOINED is listened for before calling joinLobby (see above).
const response = await Wavedash.listAvailableLobbies(); // pass true for friends only
if (response.success && response.data.length > 0) {
  await Wavedash.joinLobby(response.data[0].lobbyId);
}
```
</CodeGroup>

## Leaving a lobby

Call `leaveLobby()` when the player intentionally exits — returning to the main menu, disconnecting, or swapping rooms. The server removes them from the lobby and notifies the remaining members via `LOBBY_USERS_UPDATED`. If the player was the host, hosting transfers to the next member automatically.

<CodeGroup>
```gdscript Godot
await WavedashSDK.leave_lobby(lobby_id)
```

```csharp Unity
await Wavedash.SDK.LeaveLobby(lobbyId);
```

```lua Defold
local co = coroutine.create(function()
    wavedash.leave_lobby_async(lobby_id)
end)
assert(coroutine.resume(co))
```

```javascript JavaScript
await Wavedash.leaveLobby(lobbyId);
```
</CodeGroup>

<Note>
You do not need to call `leaveLobby()` when the tab closes — the session ends and the server cleans up membership automatically.
</Note>

## Lobby users

<CodeGroup>
```gdscript Godot
var users = WavedashSDK.get_lobby_users(lobby_id)
var host_id = WavedashSDK.get_lobby_host_id(lobby_id)
var count = WavedashSDK.get_num_lobby_users(lobby_id)
```

```csharp Unity
var users = Wavedash.SDK.GetLobbyUsers(lobbyId);
var hostId = Wavedash.SDK.GetLobbyHostId(lobbyId);
int count = Wavedash.SDK.GetNumLobbyUsers(lobbyId);
```

```lua Defold
local users = wavedash.get_lobby_users(lobby_id)
local host_id = wavedash.get_lobby_host_id(lobby_id)
local count = wavedash.get_num_lobby_users(lobby_id)
```

```javascript JavaScript
const users = Wavedash.getLobbyUsers(lobbyId);
const hostId = Wavedash.getLobbyHostId(lobbyId);
const count = Wavedash.getNumLobbyUsers(lobbyId);
```
</CodeGroup>

## Messaging

<CodeGroup>
```gdscript Godot
WavedashSDK.send_lobby_chat_message(lobby_id, message)

func _ready():
    WavedashSDK.lobby_message.connect(_on_lobby_message)

func _on_lobby_message(payload):
    print(payload["username"], ": ", payload["message"])
```

```csharp Unity
Wavedash.SDK.SendLobbyChatMessage(lobbyId, message);

void Awake() { Wavedash.SDK.OnLobbyMessage += HandleMessage; }
void HandleMessage(Dictionary<string, object> data)
{
    Debug.Log($"{data["username"]}: {data["message"]}");
}
```

```lua Defold
function init(self)
    wavedash.init({}, function(_, event, payload)
        if event == wavedash.EVENT_LOBBY_MESSAGE then
            print(payload.username .. ": " .. payload.message)
        end
    end)

    wavedash.send_lobby_message(lobby_id, message)
end
```

```javascript JavaScript
Wavedash.sendLobbyMessage(lobbyId, "Hello everyone!");

Wavedash.on(Wavedash.Events.LOBBY_MESSAGE, (payload) => {
  console.log(`${payload.username}: ${payload.message}`);
});
```
</CodeGroup>

<Note>
Messages have a maximum length of 500 characters.
</Note>

## Invites

<CodeGroup>
```gdscript Godot
WavedashSDK.invite_user_to_lobby(lobby_id, user_id)

func _ready():
    WavedashSDK.sent_lobby_invite.connect(func(response): print("Sent: ", response.success))
    WavedashSDK.lobby_invite.connect(_on_lobby_invite)

func _on_lobby_invite(data):
    WavedashSDK.join_lobby(data["lobbyId"])
```

```csharp Unity
await Wavedash.SDK.InviteUserToLobby(lobbyId, userId);

void Awake() { Wavedash.SDK.OnLobbyInvite += HandleInvite; }
void HandleInvite(Dictionary<string, object> data)
{
    Debug.Log($"Invited by: {data["inviterUsername"]}");
}
```

```lua Defold
function init(self)
    wavedash.init({}, function(_, event, payload)
        if event == wavedash.EVENT_LOBBY_INVITE then
            local co = coroutine.create(function()
                wavedash.join_lobby_async(payload.lobbyId)
            end)
            assert(coroutine.resume(co))
        end
    end)

    wavedash.invite_user_to_lobby_async(lobby_id, user_id)
end
```

```javascript JavaScript
await Wavedash.inviteUserToLobby(lobbyId, friendUserId);

Wavedash.on(Wavedash.Events.LOBBY_INVITE, (payload) => {
  Wavedash.joinLobby(payload.lobbyId);
});
```
</CodeGroup>

## Metadata

Only the host can set metadata. Any member can read it.

Godot and Unity expose **typed getters and setters** (one per value type). JavaScript and Defold pass values through a single polymorphic pair.

<CodeGroup>
```gdscript Godot
# Strings
WavedashSDK.set_lobby_data_string(lobby_id, "gameMode", "deathmatch")
var mode = WavedashSDK.get_lobby_data_string(lobby_id, "gameMode")

# Ints
WavedashSDK.set_lobby_data_int(lobby_id, "round", 3)
var round = WavedashSDK.get_lobby_data_int(lobby_id, "round")

# Floats
WavedashSDK.set_lobby_data_float(lobby_id, "matchTimer", 120.0)
var timer = WavedashSDK.get_lobby_data_float(lobby_id, "matchTimer")

WavedashSDK.delete_lobby_data(lobby_id, "gameMode")
```

```csharp Unity
// Strings
Wavedash.SDK.SetLobbyData(lobbyId, "gameMode", "deathmatch");
string mode = Wavedash.SDK.GetLobbyDataString(lobbyId, "gameMode");

// Ints
Wavedash.SDK.SetLobbyData(lobbyId, "round", 3);
int round = Wavedash.SDK.GetLobbyDataInt(lobbyId, "round");

// Floats
Wavedash.SDK.SetLobbyData(lobbyId, "matchTimer", 120.0f);
float timer = Wavedash.SDK.GetLobbyDataFloat(lobbyId, "matchTimer");

Wavedash.SDK.DeleteLobbyData(lobbyId, "gameMode");
```

```lua Defold
wavedash.set_lobby_data(lobby_id, "gameMode", "deathmatch")
wavedash.set_lobby_data(lobby_id, "round", 3)
wavedash.set_lobby_data(lobby_id, "matchTimer", 120.0)

local mode = wavedash.get_lobby_data(lobby_id, "gameMode")
wavedash.delete_lobby_data(lobby_id, "gameMode")
```

```javascript JavaScript
Wavedash.setLobbyData(lobbyId, "gameMode", "deathmatch");
Wavedash.setLobbyData(lobbyId, "round", 3);
Wavedash.setLobbyData(lobbyId, "matchTimer", 120.0);

const mode = Wavedash.getLobbyData(lobbyId, "gameMode");
Wavedash.deleteLobbyData(lobbyId, "gameMode");
```
</CodeGroup>

<Note>
`SetLobbyData` is overloaded in Unity — the correct typed variant is picked at compile time based on the value argument. For reads, use the `GetLobbyData{String,Int,Float}` variant that matches the stored type.
</Note>

## Invite links

Generate a shareable invite link for the current lobby. Pass `true` to also copy it to the user's clipboard.

<CodeGroup>
```gdscript Godot
func _ready():
    WavedashSDK.got_lobby_invite_link.connect(_on_invite_link)

func copy_link():
    WavedashSDK.get_lobby_invite_link(true)

func _on_invite_link(response):
    if response.get("success", false):
        print("Invite link: ", response["data"])
```

```csharp Unity
string link = await Wavedash.SDK.GetLobbyInviteLink(copyToClipboard: true);
Debug.Log($"Invite link: {link}");
```

```lua Defold
local co = coroutine.create(function()
    local response = wavedash.get_lobby_invite_link_async(true)
    if response.success then
        print("Invite link:", response.data)
    end
end)
assert(coroutine.resume(co))
```

```javascript JavaScript
const response = await Wavedash.getLobbyInviteLink(true);
if (response.success) {
  console.log("Invite link:", response.data);
}
```
</CodeGroup>

When a player opens an invite link, the lobby ID is passed to your game as a [launch param](/sdk/setup#launch-params). Your game must check for it on startup and join the lobby — it does not happen automatically:

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

```csharp Unity
var parameters = Wavedash.SDK.GetLaunchParams();
if (parameters.TryGetValue("lobby", out var lobbyId))
    await Wavedash.SDK.JoinLobby(lobbyId);
```

```lua Defold
local co = coroutine.create(function()
    local params = wavedash.get_launch_params()
    if params and params.lobby then
        wavedash.join_lobby_async(params.lobby)
    end
end)
assert(coroutine.resume(co))
```

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

## Events

| Event | Description |
|-------|-------------|
| `LOBBY_JOINED` | Successfully joined a lobby |
| `LOBBY_USERS_UPDATED` | A user joined or left |
| `LOBBY_MESSAGE` | New message received |
| `LOBBY_DATA_UPDATED` | Metadata changed |
| `LOBBY_KICKED` | Removed from lobby |
| `LOBBY_INVITE` | Received lobby invitation |

### Handling lifecycle events

Subscribe to lobby-lifecycle events so your UI stays in sync with membership and metadata changes.

<CodeGroup>
```gdscript Godot
func _ready():
    WavedashSDK.lobby_users_updated.connect(_on_users_updated)
    WavedashSDK.lobby_data_updated.connect(_on_data_updated)
    WavedashSDK.lobby_kicked.connect(_on_kicked)

func _on_users_updated(payload):
    print("User ", payload["username"], " ", payload["changeType"])

func _on_data_updated(metadata):
    print("Lobby metadata: ", metadata)

func _on_kicked(payload):
    print("Kicked: ", payload["reason"])
```

```csharp Unity
void Awake()
{
    Wavedash.SDK.OnLobbyUsersUpdated += data =>
        Debug.Log($"User {data["username"]} {data["changeType"]}");
    Wavedash.SDK.OnLobbyDataUpdated += metadata =>
        Debug.Log($"Lobby metadata: {metadata}");
    Wavedash.SDK.OnLobbyKicked += data =>
        Debug.Log($"Kicked: {data["reason"]}");
}
```

```lua Defold
function init(self)
    wavedash.init({}, function(_, event, payload)
        if event == wavedash.EVENT_LOBBY_USERS_UPDATED then
            print("User", payload.username, payload.changeType)
        elseif event == wavedash.EVENT_LOBBY_DATA_UPDATED then
            print("Lobby metadata:", payload)
        elseif event == wavedash.EVENT_LOBBY_KICKED then
            print("Kicked:", payload.reason)
        end
    end)
end
```

```javascript JavaScript
Wavedash.on(Wavedash.Events.LOBBY_USERS_UPDATED, (payload) => {
  console.log(`User ${payload.username} ${payload.changeType}`);
});
Wavedash.on(Wavedash.Events.LOBBY_DATA_UPDATED, (payload) => {
  console.log("Lobby metadata:", payload);
});
Wavedash.on(Wavedash.Events.LOBBY_KICKED, (payload) => {
  console.log("Kicked:", payload.reason);
});
```
</CodeGroup>
