Skip to content

Steam Lobbies and P2P

Multiplayer features with Steam networking.

Lobbies

requestLobbyList()

Search for available lobbies. Returns an array of lobby ID strings.

javascript
const lobbies = await steam.requestLobbyList();
// ['109775241234567890', '109775241234567891', ...]

Returns: string[] — array of lobby Steam IDs

createLobby(type, maxMembers)

Create a new lobby.

javascript
const lobbyId = await steam.createLobby(2, 4); // Public, 4 players

Lobby Types:

  • 0 - Private
  • 1 - Friends Only
  • 2 - Public
  • 3 - Invisible

joinLobby(lobbyId)

Join an existing lobby.

javascript
await steam.joinLobby(lobbyId);

leaveLobby()

Leave current lobby.

javascript
await steam.leaveLobby();

getLobbyData(key)

Get lobby metadata from current lobby.

javascript
const mapName = await steam.getLobbyData('map');

setLobbyData(key, value)

Set lobby metadata (owner only).

javascript
await steam.setLobbyData('map', 'forest');
await steam.setLobbyData('mode', 'deathmatch');

getLobbyMembers()

Get list of members in current lobby.

javascript
const members = await steam.getLobbyMembers();
// Array of Steam IDs

getLobbyOwner()

Get the lobby owner's Steam ID (uses current lobby).

javascript
const owner = await steam.getLobbyOwner();
// '76561198012345678'

Returns: Steam ID string, or empty string if no current lobby.

getLobbyMemberCount(lobbyId)

Get the number of members currently in a lobby.

javascript
const count = await steam.getLobbyMemberCount(lobbyId);

Parameters:

  • lobbyId - Steam lobby ID string

Returns: number — member count (0 if lobby not found or not yet joined)

getLobbyMemberByIndex(lobbyId, index)

Get the Steam ID of a specific lobby member by index.

javascript
const memberId = await steam.getLobbyMemberByIndex(lobbyId, 0); // first member

Parameters:

  • lobbyId - Steam lobby ID string
  • index - Zero-based member index

Returns: Steam ID string, or empty string if index out of range.

getLobbyMemberLimit()

Get maximum number of members allowed.

javascript
const max = await steam.getLobbyMemberLimit();

setLobbyJoinable(joinable)

Set whether the lobby can be joined.

javascript
await steam.setLobbyJoinable(false); // Lock lobby when game starts
await steam.setLobbyJoinable(true);  // Reopen

setLobbyType(type)

Change lobby visibility at runtime.

javascript
await steam.setLobbyType(0); // Make private
await steam.setLobbyType(2); // Make public again

Types: 0 Private, 1 FriendsOnly, 2 Public, 3 Invisible

inviteUserToLobby(steamId)

Invite a user to the lobby. Opens Steam's invite overlay for them.

javascript
await steam.inviteUserToLobby('76561198012345678');

Lobby discovery

requestLobbyList() now defaults to worldwide distance filter. If your previous lobbies weren't showing up for players in different regions, updating GemShell will fix it.

P2P Networking

sendP2PPacket(steamId, data, type)

Send data to another player.

javascript
const packet = JSON.stringify({ type: 'move', x: 100, y: 200 });
await steam.sendP2PPacket(targetSteamId, packet, 2);

Channel Types:

  • 0 - Unreliable
  • 1 - Unreliable, no delay
  • 2 - Reliable
  • 3 - Reliable, buffered

readP2PPacket()

Read incoming packet.

javascript
const packet = await steam.readP2PPacket();
if (packet) {
  const data = JSON.parse(packet.data);
  const sender = packet.steamId;
}

acceptP2PSessionWithUser(steamId)

Accept incoming connection.

javascript
await steam.acceptP2PSessionWithUser(steamId);

closeP2PSessionWithUser(steamId)

Close connection.

javascript
await steam.closeP2PSessionWithUser(steamId);

Examples

Host Game

javascript
async function hostGame(maxPlayers = 4) {
  // 0 = Private, 1 = FriendsOnly, 2 = Public, 3 = Invisible
  const lobbyId = await steam.createLobby(2, maxPlayers);
  
  if (lobbyId) {
    console.log('Lobby created:', lobbyId);
    const members = await steam.getLobbyMembers();
    console.log('Members:', members);
  }
  
  return lobbyId ?? null;
}

Browse Lobbies

javascript
async function findLobbies() {
  const lobbies = await steam.requestLobbyList();
  // lobbies is an array of lobby ID strings
  
  const details = [];
  for (const lobbyId of lobbies) {
    const memberCount = await steam.getLobbyMemberCount(lobbyId);
    details.push({ lobbyId, memberCount });
  }
  
  return details;
}

Full Lobby Flow

javascript
async function lobbyDemo() {
  // Create a public lobby for up to 4 players
  const lobbyId = await steam.createLobby(2, 4);
  if (!lobbyId) return;

  // Check who joined
  const members = await steam.getLobbyMembers();
  const owner = await steam.getLobbyOwner();
  console.log(`Owner: ${owner}, Members: ${members.join(', ')}`);

  // Get count using explicit ID (useful when browsing lobbies)
  const count = await steam.getLobbyMemberCount(lobbyId);
  const first = await steam.getLobbyMemberByIndex(lobbyId, 0);

  // Leave when done
  steam.leaveLobby();
}

Network Loop

javascript
function networkUpdate() {
  // Process incoming packets
  let packet;
  while ((packet = steam.readP2PPacket())) {
    handlePacket(packet.steamId, JSON.parse(packet.data));
  }
}

function handlePacket(sender, data) {
  switch (data.type) {
    case 'move':
      updatePlayerPosition(sender, data.x, data.y);
      break;
    case 'chat':
      addChatMessage(sender, data.message);
      break;
    case 'action':
      handlePlayerAction(sender, data.action);
      break;
  }
}

// Send position update
function sendPosition(x, y) {
  const packet = JSON.stringify({ type: 'move', x, y });
  for (const playerId of connectedPlayers) {
    steam.sendP2PPacket(playerId, packet, 1); // Unreliable, no delay
  }
}

Reliable Messages

javascript
async function sendReliableMessage(steamId, data) {
  const packet = JSON.stringify(data);
  await steam.sendP2PPacket(steamId, packet, 2); // Reliable
}

// Use for important events
async function onPlayerScored(steamId, points) {
  await sendReliableMessage(steamId, {
    type: 'score',
    points,
    timestamp: Date.now()
  });
}

Best Practices

  1. Use reliable for important data - Score updates, game state changes
  2. Use unreliable for frequent updates - Position, rotation
  3. Validate incoming data - Never trust client data
  4. Handle disconnections - Clean up player state
  5. Rate limit sends - Don't flood the network