Use X-Ray for custom matchmaking
This article walks you through how to use the AccelByte Gaming Services (AGS) matchmaking X-Ray tool for custom matchmaking in your game.
Prerequisites
- An understanding of Matchmaking customization.
- An environment with AGS version 3.76 or later.
- For the matchmaking X-Ray to be able to track the ticket, add the
"ticket_observability_enable": true
parameter into your matchmaking ruleset.
Send matchmaking process data
To enable the X-Ray feature to scrape the data of matchmaking tickets in your custom matchmaking, your custom matchmaking apps should call this endpoint:
POST <baseURL>/sessionhistory/v2/admin/namespaces/<namespace>/xray/tickets
There are several actions that you can use when sending the matchmaking process data. Each action shares the same payload structure, aside from the action below if the ticket is started, canceled, or expired is already sent to the session history as default.
matchFound
: use this action to track the data of a matched ticket.matchNotFound
: use this action to track the data of a ticket that is still in the queue and not is yet to be matched before the timeout period.flexed
: use this action to track the data of a ticket that is in flexed state.
Matchmaking process
This diagram shows when you should call the POST <baseURL>/sessionhistory/v2/admin/namespaces/<namespace>/xray/tickets
endpoint during the matchmaking process.
Sample code
The following pseudocode shows a common scenario of when the POST <baseURL>/sessionhistory/v2/admin/namespaces/<namespace>/xray/tickets
endpoint is called.
Pseudocode
function MakeMatches():
if isRuleFlexed == "yes":
callEndpoint("Publish ticket observability", "Flexed")
if IsMatched == "yes":
callEndpoint("Publish ticket observability", "matchFound")
else:
callEndpoint("Publish ticket observability", "matchNotFound")
X-Rays only scrape and expose data sent by the custom matchmaking. If there are fields that are not filled, they will not be displayed in the X-Ray.
Ensure that you call the endpoint every time you want to send the ticket data. In each iteration of the matchmaking process (referred to as a "loop"), you can send data multiple times within a single matchmaking ticket since the flexed
and matchNotFound
actions are sent once every loop.
Sample scenario
Here's a sample scenario for logging events the X-Ray based on 5v5 matchmaking mode.
Initiate the matchmaking process
Players enter the queue for a match, and the system begins searching for other players who meet specific criteria, such as:
- Skill level (based on ranking or past performance)
- Geographical location (to ensure low latency)
- Preferred roles (e.g., tank, support, or damage)
The system performs multiple matchmaking cycles as new players join the queue.
Flexing Rules
In this scenario, isRuleFlexed
is set to yes
(isRuleFlexed = "yes"
). After a designated waiting period (e.g., two minutes), if there are not enough players that meet the exact criteria, the system may flex certain rules to reduce wait times. For example:
- Expanding skill range: Instead of restricting matches to a narrow skill range, the system allows a broader range of skill levels to participate.
- Broadening regions: The system may relax geographical restrictions, enabling players from nearby regions with acceptable latency to join.
At this stage, custom matchmaking systems using X-Ray should log an event by calling the endpoint with the flexed
action.
Match is found
After flexing the rules, the system successfully matches two teams of five players each, meeting the relaxed criteria and resulting to a match is found scenario (isMatched = "yes"
). At this point, the custom matchmaking system should log an event by calling the endpoint with the matchFound
action.
Match not found
If no match is found (isMatched = "no"
)within a reasonable timeframe—possibly due to low player activity or an unusual time of day—the custom matchmaking system should log an event by calling the endpoint with the matchNotFound
action.
Sample request body template
{
"action": "matchFound",
"activeAllianceRule": {
"max_number": 2,
"min_number": 2,
"player_max_number": 5,
"player_min_number": 5
},
"activeMatchingRule": [
{
"attribute": "mmr",
"criteria": "distance",
"reference": 100
}
],
"function": "extend",
"gameMode": "5v5",
"isBackfillMatch": true,
"isRuleSetFlexed": true,
"iteration": 7,
"matchID": "d8140d69de02465b934281050dc0d69b",
"namespace": "GameAB",
"partyID": "0cc0cb97804b4ffd9fd8130f6e2622bb",
"remainingPlayersPerTicket": [
0
],
"remainingTickets": 0,
"sessionTickID": "3c29c2df8a0a44ac9a00afa69c14e8f7",
"tickID": 1727664260471,
"timeToMatchSec": 4.71937726,
"timestamp": "2024-07-23T07:07:56.067Z",
"unbackfillReason": "unbackfill_blocked_player_exist",
"unmatchReason": "not_enough_players"
}
This table describes each parameter in the request body.
Field name | Field description | Required | Value format | Sample Value |
---|---|---|---|---|
timestamp | Timestamp when calling the endpoint | No | Time | 2024-07-23T07:07:56.067Z |
partyID | Ticket Party ID | No | String | 0cc0cb97804b4ffd9fd8130f6e2622bb |
matchID | ID of the match, which is only filled only when a match is found | No | String | d8140d69de02465b934281050dc0d69b |
Namespace | Current namespace of the ticket | No | String | GameAB |
gameMode | Current matchpool of the ticket | No | String | 5v5 |
activeAllianceRule | Current active alliance ruleset | No | JSON | (See sample value.) |
activeMatchingRule | Current active matching ruleset | No | JSON | (See sample value.) |
function | Name of the function that called the endpoint | No | String | extend |
iteration | Total iteration before match is found | No | Integer | 7 |
TimeToMatchSec | Time to match (in seconds), which is only filled when match is found | No | Float64 | 4.71937726 |
unmatchReason | Reason when unable to find match | No | String | not_enough_players |
remainingTickets | Remaining ticket when unable to find match | No | Integer | 0 |
remainingPlayersPerTicket | Remaining players when unable to find match | No | Integer | 0 |
UnbackfillReason | Reason when unable to backfill | No | String | unbackfill_blocked_player_exist |
IsBackfillMatch | Flag to distinguish between new match and backfill match | No | Boolean | true |
IsRuleSetFlexed | Flag if ruleset is getting flexed | No | Boolean | false |
tickID | Ticket ID for the matchmaking ticket | No | Int64 | 1727664260471 |
sessionTickID | Session ticket ID to differentiate session when doing matches | No | String | 3c29c2df8a0a44ac9a00afa69c14e8f7 |
You can find the data sent by your custom matchmaking in Multiplayer > Diagnostics and Utilities > Matchmaking X-Ray on the AGS Admin Portal.
Although all of the fields in the request body are optional, they are still captured by the X-Ray. No other fields will be exposed or captured. For example, if you have a matchingRules
field that depends on a specific composition for matching, you will need to inject its value into activeAllianceRule
.
Sample ActiveAllianceRule value
[
{
"max_number": 0,
"min_number": 0,
"player_max_number": 0,
"player_min_number": 0
}
]
Sample ActiveMatchingRule value
[
{
"attribute": "string",
"criteria": "string",
"reference": 0
}
]