Matchmaking
5Stack’s matchmaking system automatically pairs players for competitive matches based on skill rating, region latency, and preferences.
Getting Started
Navigate to the Play page to access matchmaking features. You’ll see options for:
- Automated matchmaking queues
- Custom match creation
- Open matches to join
- Tournament registration
You must be authenticated with Steam and not have any active bans or cooldowns to use matchmaking.
Match Types
5Stack supports multiple competitive formats:
Competitive
Standard 5v5 matches on competitive maps with full rules
Wingman
Fast-paced 2v2 matches on smaller maps
Duel
Intense 1v1 matches for individual skill testing
Match Type Configuration
// Available match types from Matchmaking.vue:262-278
e_match_types: {
query: generateQuery({
e_match_types: [
{
where: {
value: {
_in: [
e_match_types_enum.Competitive,
e_match_types_enum.Wingman,
e_match_types_enum.Duel,
]
}
}
},
{ value: true, description: true }
]
})
}
Match types may be enabled or disabled by server administrators. Check the Play page to see currently available queues.
Region Selection and Latency
5Stack uses region-based matchmaking to ensure low-latency matches.
How Latency Testing Works
Automatic Testing
When you visit the Play page, 5Stack automatically tests your latency to all available regions
WebRTC Measurement
Uses WebRTC data channels to measure actual round-trip time to game servers
Result Storage
Latency results are cached in your browser’s local storage
Preference Selection
Regions below your acceptable latency threshold are automatically preferred
Latency Testing Implementation
// From MatchmakingStore.ts:359-390
async function getLatency(region: string) {
return new Promise(async (resolve) => {
setTimeout(() => {
resolve(undefined);
}, 5000);
try {
const buffer = new Uint8Array([0x01]).buffer;
const datachannel = await webrtc.connect(region, (data) => {
if (data === "") {
datachannel.send(buffer);
return;
}
const event = JSON.parse(data) as {
type: string;
data: Record<string, unknown>;
};
if (event.type === "latency-results") {
datachannel.close();
latencies.value.set(region, event.data);
}
});
datachannel.send("latency-test");
} catch (error) {
console.error(`Failed to get latency for ${region}`, error);
resolve(undefined);
}
});
}
Managing Region Preferences
Automatic Selection
Manual Selection
Latency Display
By default, 5Stack queues you in all regions where your latency is below the acceptable threshold (default: 75ms).// Preferred regions calculation from MatchmakingStore.ts:433-495
const preferredRegions = computed(() => {
const availableRegions = useApplicationSettingsStore()
.availableRegions.filter((region) => {
const regionLatency = getRegionlatencyResult(region.value);
if (regionLatency && region.is_lan && regionLatency.isLan) {
return true;
}
if (regionLatency &&
parseFloat(regionLatency?.latency) >
parseFloat(maxAcceptableLatency || "75")) {
return false;
}
return true;
});
// Sort by latency
return availableRegions.sort((a, b) => {
if (a.is_lan && !b.is_lan) return -1;
if (!a.is_lan && b.is_lan) return 1;
return Number(getRegionlatencyResult(a.value)?.latency) -
Number(getRegionlatencyResult(b.value)?.latency);
});
});
Click Settings on the Play page to customize your region preferences:
- Click the settings icon
- Toggle regions on/off
- Adjust maximum acceptable latency
- Click Refresh Latencies to re-test
// Toggle region preference from MatchmakingStore.ts:392-403
function togglePreferredRegion(region: string) {
const index = storedRegions.value.indexOf(region);
if (index !== -1) {
storedRegions.value.splice(index, 1);
} else {
storedRegions.value.push(region);
}
localStorage.setItem(
PREFERRED_REGIONS_KEY,
JSON.stringify(storedRegions.value.filter(Boolean))
);
}
Each region shows:
- Region name
- Current latency (ms)
- LAN indicator (if applicable)
- Selection checkbox
Regions are color-coded:
- Green: Excellent latency (< 50ms)
- Yellow: Good latency (50-75ms)
- Red: High latency (> 75ms)
LAN regions (local network servers) are automatically prioritized when detected, regardless of latency thresholds.
Joining a Queue
Queue Selection Process
Choose Match Type
Click on a match type card (Competitive, Wingman, or Duel)
Confirm Selection
Click the same card again within 5 seconds to confirm your choice
Enter Queue
You’re added to the matchmaking queue for all your preferred regions
// Queue join handler from Matchmaking.vue:344-374
handleMatchTypeClick(matchType: e_match_types_enum): void {
if (this.preferredRegions.length === 0) {
this.showSettings = true;
toast({
title: this.$t("matchmaking.no_preferred_regions") as string,
variant: "destructive",
});
return;
}
if (this.pendingMatchType === matchType) {
// Second click - confirm
if (this.confirmationTimeout) {
clearTimeout(this.confirmationTimeout);
this.confirmationTimeout = undefined;
}
this.joinMatchmaking(this.pendingMatchType);
this.pendingMatchType = undefined;
return;
}
// First click - show confirmation state
if (this.confirmationTimeout) {
clearTimeout(this.confirmationTimeout);
}
this.pendingMatchType = matchType;
this.confirmationTimeout = setTimeout(() => {
this.pendingMatchType = undefined;
this.confirmationTimeout = undefined;
}, 5000);
}
Queue Status Display
While in queue, you’ll see:
Queue Information
- Match type you’re queuing for
- Time spent in queue
- Number of players in queue
- Selected regions
Queue Actions
- Cancel: Leave the queue
- Queue automatically cancels if match found
- Can’t join multiple queues simultaneously
// Queue state from MatchmakingStore.ts:23-42
const joinedMatchmakingQueues = ref<{
details?: {
totalInQueue: number;
type: e_match_types_enum;
regions: Array<string>;
};
confirmation?: {
matchId: string;
isReady: boolean;
expiresAt: string;
confirmed: number;
confirmationId: string;
type: e_match_types_enum;
region: string;
players: number;
};
}>({});
Match Found
When a match is found:
Match Confirmation
You’ll receive a notification that a match has been found
Accept Prompt
Click Accept within the time limit (usually 30 seconds)
Waiting for Others
Wait for all players to accept
Match Ready
Once all players accept, you’re redirected to the match lobby
Important: If you fail to accept a match, you’ll receive a temporary matchmaking cooldown. Repeated failures may result in longer cooldowns.
Confirmation Display
The confirmation screen shows:
- Match type and region
- Number of confirmed players
- Time remaining to accept
- Large Accept button
Lobbies and Invites
Creating a Lobby
You can create a pre-made lobby before queuing:
// Lobby creation from MatchmakingStore.ts:269-283
const createLobby = async () => {
const { data } = await getGraphqlClient().mutate({
mutation: typedGql("mutation")({
insert_lobbies_one: [
{
object: {},
},
{
id: true,
},
],
}),
});
return data.insert_lobbies_one.id;
};
Inviting Friends
Lobby Invites
Match Invites
To invite friends to your lobby:
- View your friends list
- Click Invite next to a friend’s name
- They receive an invite notification
- Once they accept, they join your lobby
- Queue together as a party
// Invite to lobby from MatchmakingStore.ts:285-309
const inviteToLobby = async (steam_id: string) => {
const me = useAuthStore().me;
let lobby_id = me?.current_lobby_id;
if (!lobby_id) {
lobby_id = await createLobby();
}
await getGraphqlClient().mutate({
mutation: typedGql("mutation")({
insert_lobby_players_one: [
{
object: {
steam_id,
lobby_id,
},
},
{ __typename: true },
],
}),
});
};
If you have an active match, you can invite friends directly:
- Invited players bypass matchmaking
- Must be accepted before match starts
- Team balance is maintained
Lobby Features
Lobby Settings
- Set party captain
- Configure privacy (Open, Friends, Private)
- View all lobby members
Lobby Chat
- Text chat with lobby members
- Voice communication setup
- Ready status indicators
Friends System
The matchmaking store tracks your friends and their online status:
// Friend subscription from MatchmakingStore.ts:78-148
const subscribeToFriends = async (mySteamId: bigint) => {
const subscription = getGraphqlClient().subscribe({
query: generateSubscription({
my_friends: [
{},
{
elo: true,
name: true,
role: true,
country: true,
steam_id: true,
avatar_url: true,
status: true,
invited_by_steam_id: true,
player: {
is_in_lobby: true,
is_in_another_match: true,
lobby_players: [
{
limit: 1,
where: {
lobby: {
access: {
_in: [e_lobby_access_enum.Friends, e_lobby_access_enum.Open]
}
}
}
},
lobbyFields
]
}
}
]
})
});
};
Friend Status Indicators
- Online: Friend is connected to 5Stack
- In Lobby: Friend is in a pre-game lobby
- In Match: Friend is currently playing
- Offline: Friend is not connected
Matchmaking Restrictions
Cooldowns and Bans
You may be temporarily or permanently restricted from matchmaking for:
Common Restrictions
- Abandonment: Leaving matches early
- AFK Detection: Being inactive during matches
- Toxicity: Reports from other players
- Failed Accepts: Not accepting found matches
<!-- Ban check from Matchmaking.vue:18-24 -->
<template v-if="me.is_banned">
<Alert class="my-3">
<AlertDescription class="flex items-center gap-2">
<AlertTriangle class="h-4 w-4" />
{{ $t("matchmaking.banned") }}
</AlertDescription>
</Alert>
</template>
Temporary Cooldowns
Cooldowns are time-based restrictions:
<template v-else-if="me.matchmaking_cooldown">
<Alert class="my-3">
<AlertDescription class="flex items-center gap-2">
<AlertTriangle class="h-4 w-4" />
{{ $t("matchmaking.temp_banned", { time: me.matchmaking_cooldown }) }}
</AlertDescription>
</Alert>
</template>
Cooldown durations increase with repeated offenses:
- 1st offense: 30 minutes
- 2nd offense: 2 hours
- 3rd offense: 24 hours
- 4th+ offense: 7 days
Queue Statistics
The Play page shows real-time queue statistics:
// Queue stats display from Matchmaking.vue:413-435
inQueueStas() {
const inQueue = {
[e_match_types_enum.Duel]: 0,
[e_match_types_enum.Wingman]: 0,
[e_match_types_enum.Competitive]: 0,
};
const regions = this.preferredRegions as Region[];
for (let i = 0; i < regions.length; i++) {
const region: Region = regions[i];
const regionStats = this.regionStats[region.value];
if (!regionStats) {
continue;
}
inQueue[e_match_types_enum.Duel] +=
regionStats[e_match_types_enum.Duel] || 0;
inQueue[e_match_types_enum.Wingman] +=
regionStats[e_match_types_enum.Wingman] || 0;
inQueue[e_match_types_enum.Competitive] +=
regionStats[e_match_types_enum.Competitive] || 0;
}
return inQueue;
}
Each match type card displays the number of players currently in queue for your selected regions.
Custom Matches
If you don’t want to use automated matchmaking, you can create a custom match:
Click Custom Match
Scroll to the Custom Match section on the Play page
Configure Settings
Choose map, game mode, and match options
Invite Players
Invite specific players or make the match public
Start Match
Once all players are ready, start the match
Custom matches may not affect your ELO rating depending on server configuration.
Troubleshooting
- Expand your region selection
- Increase maximum acceptable latency
- Try queueing during peak hours
- Check that your selected match type is active
- Ensure you have at least one region selected
- Check for active bans or cooldowns
- Verify your latency tests completed
- Try refreshing region latencies
- Check your internet connection
- Ensure you clicked Accept in time
- Verify you’re not in another match
- Contact support if issue persists
Next Steps
Match Lobbies
Learn about match lobby features
Teams
Create a team for organized play
Statistics
Track your competitive performance