Skip to main content

Queues

A queue defines a set of matchmaking rules that can be used to assemble a group of clients that have joined the queue into a match, and also defines how those matched players are assigned a server. Each queue belongs to exactly one Project.

The simplest example is as follows, which defines a queue named speedy-racer-1999.solo that simply assigns each player that joins the queue into a 1 player match with a hard-coded server address:

{
"name": "projects/speedy-racer-1999/queues/solo",
"id": "speedy-racer-1999.solo",
"title": "Solo Queue",
"active": true,
"simple": {
"playerSettings": {
"maxPlayers": 1
},
"startSettings": {
"playersToAllocate": 1
}
},
"static": {
"server": "127.0.0.1"
}
}

The simple section indicates this queue is using simple match allocation logic.

The simple.playerSettings section defines how players will be formed into matches. In this case, maxPlayers is set at 1, which means matches will be formed of at most 1 player each.

The simple.startSettings parameter then defines the criteria that must be met for a server to be assigned. In this case, the queue will only assign a server if there's at least 1 player in the match.

Finally, the static section indicates we're using the 'static' server allocator, whereby players are simply always told to join the server at 127.0.0.1, rather than pulling from a game server provider (such as storc or Agones.) This is incredibly useful for debugging.

Orchestration

Here's a more advanced queue that allocates exactly 4 players to matches with exactly 2 teams of 2 players each, using servers provided from an existing IMS zeuz account:

{
"name": "projects/speedy-racer-1999/queues/2v2",
"id": "speedy-racer-1999.2v2",
"title": "Two-on-two Queue",
"active": true,
"simple": {
"startSettings": {
"playersToAllocate": 4
},
"playerSettings": {
"teamCount": 2,
"playersPerTeam": 2,
"maxPlayers": 4
}
},
"storc": {
"projectId": "speedy-racer-1999",
"allocationId": "a5c95500-bbe8-44d0-8381-35dc42cd3c46",
"clientPort": "GamePort"
}
}

Note the use of clientPort to define the name of the port on the assigned Payload that the matchmaker should look up (in this case, GamePort) - this allows a single machine to host multiple game servers across multiple ports.

Match Validators

Queues can optionally define a set of Match Validators to extend the match forming logic and apply additional conditions, allowing more dynamic behaviours over the basic matchmaking settings. Matches will only be formed if they adhere to the provided validators.

Currently, the matchmaker supports two basic validator extensions:

  • timeseries - allows different rules to apply based on the amount of time a player has been queuing.
  • match_start - allows the match start criteria to be changed (but is typically embedded into a timeseries validator, per the example below.)

For example, here is a queue that defines a 3-player-per-team queue that uses match validation rules to change the matching criteria over time:

{
"name": "projects/speedy-racer-1999/queues/3-player",
"id": "speedy-racer-1999.3-player",
"title": "3-Player Teams Queue",
"active": true,
"simple": {
"startSettings": {
"playersToAllocate": 12
},
"playerSettings": {
"teamCount": 4,
"playersPerTeam": 3,
"maxPlayers": 12,
"packTeams": true
}
},
"matchValidators": [
{
"name": "match start",
"extension": "timeseries",
"config": {
"timeseries": {
"extension": "match_start",
"buckets": [
{
"validation": {
"startSettings": {
"playersToAllocate": 6
}
}
},
{
"duration": 30,
"validation": {
"startSettings": {
"playersToAllocate": 12
}
}
}
]
}
}
}
]
}

Note the use of timeseries rules that loosen the matching criteria after 30 seconds to allow a match with at least 6 people, rather than the ideal 12.

The simple.playerSettings.packTeams value tells the matchmaker to favour full-sized teams, which is important if there aren't enough people in the queue to form a full match. For instance, if there were only 9 players available with which to form the match, this ensures it creates exactly 3 teams of 3 players each, rather than 4 unbalanced teams of 2 or 3 players each.

Accept/Reject Behaviours

Queues can optionally require that all users confirm they want to proceed with a match after it has been formed by specifying a accept timeout for users:

{
"name": "projects/speedy-racer-1999/queues/solo",
"id": "speedy-racer-1999.solo",
"title": "Solo Queue",
"active": true,
"accept": {
"acceptTimeout": "30s"
},
"simple": {
"playerSettings": {
"maxPlayers": 1
},
"startSettings": {
"playersToAllocate": 1
}
},
"static": {
"server": "127.0.0.1"
}
}

With this configuration, accept.acceptTimeout indicates that players will have 30s to confirm they wish to accept the match - only once all players have accepted will game server information be provided to them.

Any player that rejects the match (or fails to respond) will be automatically be removed from the queue, and all other remaining players will automatically re-queue with their original search parameters and queueing time.

Data Sources

Data source declarations can be used to retrieve user-specific data and make it available to the matchmaking algorithm, for instance when using skill-based matchmaking.

{
"name": "projects/speedy-racer-1999/queues/solo",
"id": "speedy-racer-1999.solo",
"title": "Solo Queue",
"active": true,
"simple": {
"playerSettings": {
"maxPlayers": 1
},
"startSettings": {
"playersToAllocate": 1
}
},
"dataSource": {
"playFab": {
"totalPlayTime": {
"valuePath": "played_time",
"default": 0,
"readOnlyDataKey": "PlayerInfo"
}
}
},
"static": {
"server": "127.0.0.1"
}
}

In the above example, when a user joins a Queue, the value of played_time from a data object at key PlayerInfo stored in their PlayFab user account is retrieved and made available to the matchmaking system. It can then be referenced by various matchmaking validators by specifying the path we have defined for it, totalPlayTime.

Client Latency

If the client provides client latency measurements in their join request, these data can be used to assign them to a server located nearer to them when used in conjunction with a location-aware platform.

In the example below, the client_latency extension is specified as a data point, and will ensure that clients are only assigned to servers that are in locations with a measured latency of 100ms or lower:

{
"name": "projects/speedy-racer-1999/queues/duo",
"id": "speedy-racer-1999.solo",
"title": "Solo Queue",
"active": true,
"platform": {
"platformReference": {
"name": "projects/speedy-racer-1999/platforms/location-aware"
}
},
"simple": {
"playerSettings": {
"maxPlayers": 2
},
"startSettings": {
"playersToAllocate": 2
}
},
"dataPoints": [
{
"name": "location match",
"extension": "client_latency",
"config": {
"clientLatency": {
"minimumMillis": 0,
"maximumMillis": 100
}
}
}
]
}

This behaviour can be extended with the timeseries extension to expand the allowable search parameters over time.

In the below example, for the first 15 seconds a player is queued they can only be assigned to servers in locations that measure up to 100ms away from them (based on the data in their join request). For the next 45 seconds, this will be expanded to locations measured up to 200ms away (with a preference for those under 100ms), and then for the next 120s they will be matched with servers in locations that are measured up to 1,000ms (1s) away (with a preference for those under 200ms.)

{
"name": "projects/speedy-racer-1999/queues/duo",
"id": "speedy-racer-1999.solo",
"title": "Solo Queue",
"active": true,
"platform": {
"platformReference": {
"name": "projects/speedy-racer-1999/platforms/location-aware"
}
},
"simple": {
"playerSettings": {
"maxPlayers": 2
},
"startSettings": {
"playersToAllocate": 2
}
},
"dataPoints": [
{
"name": "location match",
"extension": "timeseries",
"weight": 1,
"config": {
"timeseries": {
"extension": "client_latency",
"buckets": [
{
"duration": 15,
"datapoint": {
"clientLatency": {
"minimumMillis": 0,
"maximumMillis": 100
}
}
},
{
"duration": 45,
"datapoint": {
"clientLatency": {
"minimumMillis": 100,
"maximumMillis": 200
}
}
},
{
"duration": 120,
"datapoint": {
"clientLatency": {
"minimumMillis": 200,
"maximumMillis": 1000
}
}
}
]
}
}
}
]
}