Search documentation

Find pages, sections, and content across all docs.

WavedashDocs

Multiplayer lobbies

Create and manage multiplayer game 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.

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")
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");
}
Wavedash.on(Wavedash.Events.LOBBY_JOINED, (payload) => {
  console.log(`Joined ${payload.lobbyId} with ${payload.users.length} players`);
});

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.

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.

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
public async void CreateLobby()
{
    // OnLobbyJoined (wired up in Awake) fires once the player is in.
    await Wavedash.SDK.CreateLobby(
        WavedashConstants.LobbyVisibility.PUBLIC,
        maxPlayers: 4);
}
// LOBBY_JOINED fires once the player is in — handle your UI there.
await Wavedash.createLobby(Wavedash.LobbyVisibility.PUBLIC, 4);

Visibility options

ValueConstantDescription
0PUBLICAnyone can find and join
1FRIENDS_ONLYOnly friends can see and join
2PRIVATEOnly joinable with the lobby ID

In JavaScript use Wavedash.LobbyVisibility.PUBLIC / FRIENDS_ONLY / PRIVATE.

Joining and listing

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

# 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)
// 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"]);
// 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);
}

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.

await WavedashSDK.leave_lobby(lobby_id)
await Wavedash.SDK.LeaveLobby(lobbyId);
await Wavedash.leaveLobby(lobbyId);

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

Lobby users

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)
var users = Wavedash.SDK.GetLobbyUsers(lobbyId);
var hostId = Wavedash.SDK.GetLobbyHostId(lobbyId);
int count = Wavedash.SDK.GetNumLobbyUsers(lobbyId);
const users = Wavedash.getLobbyUsers(lobbyId);
const hostId = Wavedash.getLobbyHostId(lobbyId);
const count = Wavedash.getNumLobbyUsers(lobbyId);

Messaging

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"])
Wavedash.SDK.SendLobbyChatMessage(lobbyId, message);

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

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

Messages have a maximum length of 500 characters.

Invites

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"])
await Wavedash.SDK.InviteUserToLobby(lobbyId, userId);

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

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

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 passes values through a single polymorphic pair.

# 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")
// 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");
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");

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.

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

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"])
string link = await Wavedash.SDK.GetLobbyInviteLink(copyToClipboard: true);
Debug.Log($"Invite link: {link}");
const response = await Wavedash.getLobbyInviteLink(true);
if (response.success) {
  console.log("Invite link:", response.data);
}

When a player opens an invite link, the lobby ID is passed to your game as a launch param. Your game must check for it on startup and join the lobby — it does not happen automatically:

var params = WavedashSDK.get_launch_params()
if params.has("lobby"):
    WavedashSDK.join_lobby(params["lobby"])
var parameters = Wavedash.SDK.GetLaunchParams();
if (parameters.TryGetValue("lobby", out var lobbyId))
    await Wavedash.SDK.JoinLobby(lobbyId);
const params = Wavedash.getLaunchParams();
if (params.lobby) {
  await Wavedash.joinLobby(params.lobby);
}

Events

EventDescription
LOBBY_JOINEDSuccessfully joined a lobby
LOBBY_USERS_UPDATEDA user joined or left
LOBBY_MESSAGENew message received
LOBBY_DATA_UPDATEDMetadata changed
LOBBY_KICKEDRemoved from lobby
LOBBY_INVITEReceived lobby invitation

Handling lifecycle events

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

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"])
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"]}");
}
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);
});