Match lobbies provide a central communication hub for players, organizers, and spectators before and during matches. The lobby system includes text chat, voice communication, and presence tracking.
Accessing the Lobby
Lobby access is determined by your role in the match:
computed : {
canJoinLobby () {
if ( ! this . match ) return false ;
// Only active matches have lobbies
if ( ! [
e_match_status_enum . Live ,
e_match_status_enum . PickingPlayers ,
e_match_status_enum . Scheduled ,
e_match_status_enum . Veto ,
e_match_status_enum . WaitingForCheckIn ,
e_match_status_enum . WaitingForServer ,
]. includes ( this . match . status )) {
return false ;
}
return (
this . match . is_in_lineup ||
this . match . is_organizer ||
this . match . is_coach
);
},
}
Lobbies are automatically shown on the match page for eligible users. No separate connection required.
Lobby Chat
The chat system is implemented via ChatLobby.vue:
< ChatLobby
class = "max-h-96"
instance = "matches/id"
type = "match"
: lobby-id = " match . id "
: play-notification-sound = " match . status !== e_match_status_enum . Live "
v-if = " canJoinLobby "
/>
Features
Real-time Messages Instant message delivery using WebSocket subscriptions
User Presence See who’s online and in-game in real-time
Notifications Sound alerts for new messages (configurable)
Message History Persistent chat history throughout match lifecycle
Presence Tracking
The match lobby store tracks user presence:
export const useMatchLobbyStore = defineStore ( "matchLobby" , () => {
const lobbyChat = ref < Record < string , Map < string , unknown >>>({});
const add = (
matchId : string ,
user : {
name : string ;
steam_id : string ;
avatar_url : string ;
inGame : boolean ;
},
) => {
if ( ! lobbyChat . value [ matchId ]) {
lobbyChat . value [ matchId ] = new Map ();
}
lobbyChat . value [ matchId ]. set ( user . steam_id , user );
};
const set = (
matchId : string ,
users : Array <{ steam_id : string ; name : string ; avatar_url : string }>,
) => {
if ( ! lobbyChat . value [ matchId ]) {
lobbyChat . value [ matchId ] = new Map ();
}
for ( const user of users ) {
lobbyChat . value [ matchId ]. set ( user . steam_id , user );
}
};
const remove = ( matchId : string , user : { steam_id : string }) => {
lobbyChat . value [ matchId ]?. delete ( user . steam_id );
};
return { lobbyChat , add , set , remove };
});
User States
User is connected to the lobby:
Avatar displayed in online users list
Can send and receive messages
Presence indicator shown
User is connected to the game server:
Special indicator shown
Still receives chat messages
May have delayed responses
User disconnected:
Removed from online users list
Messages not delivered in real-time
Can catch up when reconnecting
Notification Settings
Control notification behavior:
< ChatLobby
: play-notification-sound = " match . status !== e_match_status_enum . Live "
/>
Notifications enabled:
Sound alerts for new messages
Important for coordination
Helps ensure players are ready
Notifications typically muted:
Players focused on game
Reduces distractions
Chat still accessible
Users can override notification settings in their personal preferences.
Match-specific header shows key information:
< ChatMatchHeader : match = " match " />
Displays:
Match status
Team names and scores
Quick actions (connect to server, etc.)
Time remaining (for scheduled matches)
Lobby Access Control
Organizers can control lobby access:
< template >
< Popover v-model : open = " popoverOpen " >
< div v-if = " match . invite_code " >
< ClipBoard : data = " matchInviteLink " />
< span > {{ match . invite_code }} </ span >
</ div >
< PopoverTrigger >
< Button variant = "outline" size = "icon" >
< component : is = " getIcon ( match . options . lobby_access ) " />
</ Button >
</ PopoverTrigger >
< PopoverContent >
< div class = "flex justify-center" >
< Button
v-for = " access in [
e_lobby_access_enum . Open ,
e_lobby_access_enum . Friends ,
e_lobby_access_enum . Invite ,
e_lobby_access_enum . Private ,
] "
@ click = " updateLobbyAccess ( access ) "
: variant = " match . options . lobby_access === access ? 'default' : 'outline' "
>
< component : is = " getIcon ( access ) " />
{{ access }}
</ Button >
</ div >
</ PopoverContent >
</ Popover >
</ template >
Access Icons
getIcon ( access : e_lobby_access_enum ) {
switch ( access ) {
case e_lobby_access_enum . Private :
return Lock ; // 🔒
case e_lobby_access_enum . Invite :
return Send ; // ✉️
case e_lobby_access_enum . Open :
return Unlock ; // 🔓
case e_lobby_access_enum . Friends :
return Handshake ; // 🤝
}
}
Invite Code Sharing
For invite-only lobbies:
computed : {
matchInviteLink () {
const currentRoute = new URL ( window . location . href );
currentRoute . search = "" ;
const baseUrl = currentRoute . toString ();
return ` ${ baseUrl } ?invite= ${ this . match . invite_code } ` ;
},
}
Generate Link
Copy to Clipboard
const inviteLink = `https://5stack.gg/matches/ ${ matchId } ?invite= ${ inviteCode } ` ;
Tournament Lobbies
Tournament matches have additional lobby features:
const subscribeToChatTournaments = async () => {
const subscription = getGraphqlClient (). subscribe ({
query: generateSubscription ({
tournaments: [
{
where: {
_or: [
{
status: { _eq: e_tournament_status_enum . Live },
joined_tournament: { _eq: true },
},
{
status: { _eq: e_tournament_status_enum . Live },
is_organizer: { _eq: true },
},
],
},
},
{
id: true ,
name: true ,
status: true ,
},
],
}),
});
subscription . subscribe ({
next : ({ data }) => {
chatTournaments . value = data ?. tournaments || [];
},
});
};
Tournament participants automatically get access to lobbies for their matches and the tournament-wide chat.
Voice Communication
While not built into the web interface, lobbies coordinate voice:
Join Discord
Most teams use Discord for voice. Link your Discord in settings.
Auto-Create Channels
Some tournaments auto-create voice channels for matches.
In-Game Voice
CS2 in-game voice is available once connected to the server.
Match Coordination Features
Quick Connect
When match is live:
< QuickMatchConnect
: match = " match "
v-if = " match . status === e_match_status_enum . Live "
/>
Provides:
One-click connect to server
Copy connection string
GOTV link for spectators
< ScheduleMatch : match = " match " v-if = " match . can_schedule " />
Shown in lobby when:
Match is in PickingPlayers status
Organizer has scheduling permissions
Displays:
Scheduled time
Countdown timer
Time zone conversions
Check-in Status
< CheckIntoMatch : match = " match " v-if = " showCheckInSection " />
Lobby shows check-in progress:
Who has checked in
Who is pending
Time remaining to check in
Live Match Lobby
During live matches, the lobby provides:
Server Status Real-time server health and player count
Score Updates Live score tracking per map
Pause Notifications Alerts when match is paused
Admin Controls Organizer tools for match management
Moderating Chat
Organizers have moderation tools:
Abusive behavior in match lobbies may result in platform-wide penalties.
Lobby Lifecycle
Lobby availability by match status:
const LOBBY_ACTIVE_STATUSES = [
e_match_status_enum . PickingPlayers ,
e_match_status_enum . Scheduled ,
e_match_status_enum . WaitingForCheckIn ,
e_match_status_enum . Veto ,
e_match_status_enum . WaitingForServer ,
e_match_status_enum . Live ,
];
Created
Lobby opens when match is created and players start joining.
Active
Lobby remains active through veto, check-in, and live play.
Closed
Lobby closes 10 minutes after match ends (for post-game discussion).
Archived
Chat history remains accessible in match details.
Next Steps
Creating Matches Set up matches with the right lobby settings
Match Options Configure lobby access and notifications
Statistics View performance data from completed matches