Lobby

Steam Lobbies are often misunderstood as multiplayer or networking features. In reality, they are chat rooms with metadata, so much so that Valve refers to them internally as “chats” rather than “lobbies.”

At their core, Steam Lobbies provide a way to group players and associate metadata with both the group (the lobby) and its members. This metadata enables lobby searching, matchmaking, and player-specific state sharing, all without requiring a direct network connection between players.

Steam’s matchmaking system is built on top of this lobby structure. A lobby acts as a shared space where data about the session (e.g., game mode, map, status) is stored as key-value pairs. Each player also stores personal metadata (e.g., loadout, readiness) visible only to other lobby members.

To summarise:

  • Lobby metadata communicates session-wide information. It is public to anyone who can see or search for the lobby.

  • Member metadata communicates per-player information. It is only accessible to other members within the same lobby.

If you’re implementing party-based play, session finding, or similar functionality, Steam Lobbies are likely what you’re looking for.

For further technical details, refer to Valve’s Steamworks Documentation on Matchmaking.

What is a Lobby?

The most important thing to understand: a Steam Lobby is not a networking system.

  • You can join and use a lobby with no network session active.

  • You can establish network connections without ever using a lobby.

  • The two systems are entirely independent.

Think of a Steam Lobby as a structured chat room with metadata. It helps players group up and exchange lightweight information, such as session intent, player loadouts, or status, before initiating an actual multiplayer session.

Steam Lobbies are commonly used as the first step in forming a party, and later as the container for matchmaking data. But the lobby itself does not facilitate multiplayer gameplay. That’s your job, through your networking layer.

Your user can be a member of more than 1 lobby, Steam allows users to be a member of 1 (Normal) lobby and up to 2 (Invisible) lobbies. It would be typical that a game would be managing at least 2 lobbies for a player.

Lobby Type

Type
Steam Class
Visibility
Join Method
Common Use Case

Private

Normal

Hidden from friends & searches

Only by direct invite

Closed sessions with specific players (e.g., invite-only co-op).

Friends Only

Normal

Visible to friends only

By friends or invite

Drop-in games for friends, no public exposure.

Public

Normal

Visible to all via search & friends list

Open or invite

Public matchmaking sessions.

Invisible

Invisible

Searchable, but hidden from friends list

Open or invite

Hidden lobbies that can be searched, but don’t appear as “playing with friends.” Useful for matchmaking where friends list exposure is undesired.

Session Lobby

This is just a regular Steam Lobby used in a particular way.

A Session Lobby is the term used for the lobby where matchmaking takes place, typically used to prepare a game session. In games like DOTA, Halo, or similar titles, this is the lobby you enter after clicking "Play." It is the space where players are matched with others either randomly or based on specific criteria, and it serves as the gateway to entering a game session.

In a Session Lobby, players are typically grouped for matchmaking purposes. The lobby enables random or pre-set players to find each other and organize the session, such as determining the game mode, map, or other preferences before the actual game starts. This is where the matchmaking process occurs, facilitating the connection between players who may not know each other but want to play together.

Key functions of a Session Lobby include:

  • Matchmaking: Finding and joining players with similar criteria or skill levels.

  • Session preparation: Players can view game settings, such as map and mode, and confirm before entering the match.

  • Random or structured grouping: Players may be matched randomly or as part of a more controlled, structured system, depending on the game’s matchmaking rules.

The Session Lobby is critical for enabling multiplayer games where players do not know each other beforehand, providing the infrastructure for matchmaking and game session preparation.

Group Lobby

This is just a regular Steam Lobby used in a particular way.

A Group Lobby is a term commonly used to describe a lobby where friends or players can group together before entering a session. It serves as a way for players to coordinate and share information about which session lobby to join together, but it is not used to create or host games directly.

In games like DOTA or Halo, the Group Lobby functions similarly to a "Fire Team" or "Party." Players in a group lobby do not initiate a game directly. Instead, they use the lobby to gather, share information, and coordinate on which game session they want to join. Once the group decides on a session, they transition into the appropriate Session Lobby for the actual gameplay.

Group lobbies are typically used for:

  • Organizing friends or teammates to play together.

  • Sharing session details like the Lobby ID or Game Server ID, or similar, they intend to join.

  • Ensuring that all group members are on the same page before joining a session.

While a Group Lobby doesn’t facilitate the creation of a game, it’s essential for games that want to manage the social aspect of matchmaking and ensure a smooth transition into the gameplay experience.

Members

Each user in a lobby is represented as a Lobby Member. Every member can attach a set of metadata to themselves, key-value pairs visible to other members of the same lobby. This metadata can only be set by the member it belongs to.

  • You can read every other member's metadata.

  • You can only write your own.

This data is not visible outside the lobby. If you're not a member, you cannot access the member metadata. It’s often used to communicate things like player loadout, readiness status, or role selection within the session.

Metadata

Lobby metadata and member metadata are the foundation of how Steam Lobbies are used for matchmaking and session setup.

All metadata is stored as key-value pairs, where both the key and value are strings:

Dictionary<string, string>

Lobby Metadata

  • Set by the lobby owner (host).

  • Readable by anyone who can see the lobby, including search results.

  • Used to describe the session: map name, game mode, rules, etc.

  • Used by the matchmaking system to filter and sort search results.

Member Metadata

  • Set by the individual member.

  • Readable only by other members of the same lobby.

  • Used to describe player-specific info: loadout, readiness, team choice, etc.

Chat

Despite being called a Lobby in the public-facing Steam API, on Valve’s backend, it's referred to as a chat room — because that's what it is under the hood.

Core Concepts

  • A Steam Lobby is a chat room with built-in metadata and structured membership.

  • You can send and receive messages between lobby members without a network connection.

  • This system is separate from your game’s networking — it’s Steam-to-Steam communication.

Message Format

Lobby chat messages are sent as raw byte[] data. It’s up to your game to define what that data represents — e.g. plain text, JSON, binary instructions, etc.

  • Messages are only delivered to current members of the lobby.

  • There’s no built-in guarantee of order, delivery, or reliability — treat it as lightweight messaging.

  • Ideal for session coordination like ready checks, map voting, or status updates before starting a match.

Use Cases

  • Displaying lobby chat text in a UI.

  • Exchanging ready/check-in states.

  • Syncing small pre-match data (e.g. character selections).

For structured handling of this feature, consider implementing a Lobby Chat Director system in your game to route and interpret messages.

Examples

Create

Code Free

C#

// Generic Create Function where you pass in an enumerator to set the type
ELobbyType type; //= Set the type of lobby you want
int slots;       //= Set the number of members this lobby can have
LobbyData.Create(type, slots, HandleLobbyCreate);

// This assumes you want to create an Invisible lobby for use as a Party lobby
LobbyData.CreateParty(slots, HandleLobbyCreate)

// This assumes you want a Public lobby type for use as a Session lobby
LobbyData.CreatePublicSession(slots, HandleLobbyCreate)

// This assumes you want a Private lobby type for use as a Session lobby
LobbyData.CreatePrivateSession(slots, HandleLobbyCreate)

// This assumes you want a Friend Only lobby type for use as a Session lobby
LobbyData.CreateFriendOnlySession(slots, HandleLobbyCreate)

All options take a function as the last parameter to be called when the process is complete. The function takes the form of.

void HandleLobbyCreate(EResult result, LobbyData lobby, bool ioError)
{
    //This is called when creation is completed
}

Code Free

C#

public void SearchForLobbies()
{
    // First, define your search arguments
    SearchArguments args = new();
    args.distance = ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault;
    args.stringFilters.Add(new() { key = "SomeKey", value = "SomeValue", comparison = ELobbyComparison.k_ELobbyComparisonEqual });

    // Next use them ... in this case, we use expression 
    LobbyData.Request(args, 1, (Lobbies, IOError) =>
    {
        // Lobbies is an array of lobbies that were found
        // IOError is true if there was some error
    });
    // Or you can use a named function
    LobbyData.Request(args, 1, HandleResults);
}

private void HandleResults(LobbyData[] Lobbies, bool IOError)
{
    // Lobbies is an array of lobbies that were found
    // IOError is true if there was some error
}

Join

Code Free

C#

public void JoinLobby(LobbyData lobby)
{
    // You can simply call Join and provide the callback as expression
    lobby.Join((Result, IOError) =>
    {
        // Result.Lobby is the lobby joined if any
        // Result.Response to the response message, if any
        // Result.Locked is this lobby locked?
    });
    // Or feed it a named function
    lobby.Join(HandleJoined);
}

private void HandleJoined(LobbyEnter Result, bool IOError)
{
    // Result.Lobby is the lobby joined if any
    // Result.Response to the response message, if any
    // Result.Locked is this lobby locked?
}

Leave

Code Free

C#

public void LeaveLobby(LobbyData lobby)
{
    lobby.Leave();
}

Invite to Lobby

Invite a friend to join a specific lobby. It is important to understand that when inviting a friend, they may not accept right away. You can invite friends who are not currently playing the game or may not even own the game. See the Accept Lobby Invite for details on how to handle all the use cases your accepting user might face.

Code Free

C#

public void InviteToLobby(UserData User, LobbyData Lobby)
{
    // From the user
    User.InviteToLobby(Lobby);
    // Or, from the Lobby
    Lobby.InviteUserToLobby(User);
}

Alternatively, you can open the Steam Overlay to the Lobby Invite Dialogue and select a user to invite from there.

Overlay.Client.ActivateInviteDialog(Lobby);

Accept Lobby Invite

When a lobby invite is sent to a user, a number of things happen:

  • The user will get a notification in Steam Friend Chat with an Accept button they can click

  • If the user is in a game, they will receive the "Lobby Invite" event.


If the user clicks the "Accept" button from the Steam Friend chat a few different things can happen

  • If the user is not currently playing the game, Steam will launch the game with the lobby ID on the command line.

  • If the user is currently playing the game, the "Game Lobby Join Requested" event will be raised.

Code Free

The Lobby Invite and Game Lobby Join Requested events are available in the Steamworks Event Trigger component.

C#

If you want to handle the invite process entierly within the game then you will want to react to the Lobby Invite event.

Matchmaking.Client.EventLobbyInvite.AddListener(HandleLobbyInvite);

The handler for this takes the form of:

private void HandleLobbyInvite(LobbyInvite Response)
{
    // Response.ForGame the game the invite is for
    // Response.FromUser the user that invited you
    // Response.ToLobby the lobby you where invited to
}

Register a handler to listen on the Game Lobby Join Requested event. This event is raised when the user clicks the "Accept" button in the Steam Friend chat after receiving an invite to join a lobby.

Overlay.Client.EventGameLobbyJoinRequested.AddListener(HandleLobbyJoinRequest);

The handler for this event would look like this:

private void HandleLobbyJoinRequest(LobbyData Lobby, UserData User)
{
    // Lobby is the lobby you were invited to
    // User is the user who invited you
}

Detect Join/Leave

Code Free

If you are not using Lobby Manager, you can still do this using the Steamworks Event Trigger component.

The Lobby Chat Update event fires any time any lobby you are a member of has a user leave or join. The parameter will tell you which lobby, what event and for whom.

C#

Matchmaking.Client.EventLobbyChatUpdate.AddListener(HandleChatUpdate);

The handler takes the form of

private void HandleChatUpdate(LobbyChatUpdate_t callback)
{
    UserData who = callback.m_ulSteamIDUserChanged;
    LobbyData whatLobby = callback.m_ulSteamIDLobby;


    if((EChatMemberStateChange)callback.m_rgfChatMemberStateChange == EChatMemberStateChange.k_EChatMemberStateChangeLeft)
    {
        // The user left
    }
    else if ((EChatMemberStateChange)callback.m_rgfChatMemberStateChange == EChatMemberStateChange.k_EChatMemberStateChangeEntered)
    {
        // The user joined
    }
    else if ((EChatMemberStateChange)callback.m_rgfChatMemberStateChange == EChatMemberStateChange.k_EChatMemberStateChangeDisconnected)
    {
        // The user lost connection
    }
}

Metadata

Code Free

C#

public void MetadataUse(LobbyData Lobby)
{
    // Let's set a simple field to a simple value
    Lobby["a simple field"] = "a simple value";

    // Okay, that was easy, and now let's set that on our member
    LobbyMemberData Me = Lobby.Me;
    Me["a simple field"] = "a simple value";

    // Let's read the owner's metadata
    LobbyMemberData Owner = Lobby.Owner;
    Debug.Log($"Owner's field = {Owner["a simple field"]}");
}

Game Server

Code Free

C#

// Setting the game server to the Lobby's owner
Lobby.SetGameServer();

// Let's set the game server to a specific ID
CSteamID fakeServerID =new();
Lobby.SetGameServer(fakeServerID);

// And now let's set a server by IP:Port
Lobby.SetGameServer("0.0.0.0", 7777);

// And finally, maybe we try hard and use all of it
Lobby.SetGameServer("0.0.0.0", 7777, fakeServerID);

We can then check the Game Server

// You can read the current game server
if(Lobby.HasServer)
{
    LobbyGameServer server = Lobby.GameServer;

    CSteamID serverId = server.id;
    string ipAddress = server.IpAddress;
    ushort port = server.port;
}

You can and should also use the event system for example, you can listen when game server is set on any lobby you are a member of.

// How to know when this was done?
Matchmaking.Client.EventLobbyGameCreated.AddListener(HandleGameServerSet);

The handler for this would look like this

private void HandleGameServerSet(LobbyGameCreated_t callback)
{
    // Check this against your lobbies to see which one is needed
    LobbyData theLobbyThatWasSet = callback.m_ulSteamIDLobby;

    // You can read the current game server
    if (theLobbyThatWasSet.HasServer)
    {
        LobbyGameServer server = theLobbyThatWasSet.GameServer;

        CSteamID serverId = server.id;
        string ipAddress = server.IpAddress;
        ushort port = server.port;
    }
}

Chat (Send/Receive)

Code Free

C#

// First, you need to know the lobby to send this to
LobbyData Lobby = 123456798; // stand in for a real lobby ID

// Then you need to decide what to send
// This can be a string message
Lobby.SendChatMessage("Hello World");
// Or
// It can be a serializable structure or class
Lobby.SendChatMessage(SomeObjectIHave);

// In order to "listen" for chat messages, you should use
Matchmaking.Client.EventLobbyChatMsg.AddListener(HandleChatMessage);

The handler will take the form of

private void HandleChatMessage(LobbyChatMsg ChatMsg)
{
    // ChatMsg.Message: The string message if you sent a string
    // ChatMsg.FromJson<YourDataType>(): Gets the object if that is what you sent
    // ChatMsg.sender: who sent the message
    // ChatMsg.lobby: what lobby was it sent to
    // ChatMsg.receivedTime: when you first saw the message
    // ChatMsg.type: the EChatEntryType of message, which should always be EChatEntryType.k_EChatEntryTypeChatMsg
}

Last updated