How to Match Players

In this tutorial, we'll first explore the GameSparks matching framework to gain an understanding of how the system works to process and complete player matches on the basis of Match configurations.

With this background in place, we'll move on to create a Match configuration with customized Thresholds in the GameSparks Portal. We'll then use this configuration to perform a Match in the Test Harness that will match Players in the game. A typical example of matching players is where you want to match players based on their similar skill level in your game so that players can play against someone of equal ability to make it more enjoyable for all.

We'll also see how to enable and work with:


Matchmaking Availability? The Matchmaking feature is no longer available to games created on or after February 19th, 2019, or for games created before February 19th, 2019 that had not yet used the feature. If you require Matchmaking for your game and do not have access under your current plan, please contact us to discuss the Enterprise Pricing Tier.

The Matching Framework

Matching players in GameSparks starts when a MatchmakingRequest is issued. It proceeds according to the matching criteria the request contains, such as the player's skill level and the matching constraints built into the specific Match configuration the request uses. Matches are highly-configurable and include a timespan which defines how long the search for a match will continue. In many cases, the processing of the MatchmakingRequest will continue for some time - perhaps several minutes - before concluding in success (MatchFound) or failure (MatchNotFound).

If successful, the matchmaking process completes with a Match Instance. However, after a player has issued a MatchmakingRequest and until either a match is made or is not made, the status remains as a Pending Match. Let's follow a couple of simple examples that illustrate how the matchmaking process plays out from Pending Matches through to a Match Instance.

Example 1

1. Suppose we have 4 players and each player requests a match while playing your game. In this example, we assume that all players match on skill. The Match configuration you have built into the game imposes these matching constraints:

2. Now, suppose players 1, 2, 3 , and 4 issue their requests in this sequence:

Note: At this point the matchData is copied over from PMJ1 to MInst1:

  1. This is the only time we copy over the matchData for the Pending Match to the matchData for the Match Instance.
  2. You can change the Match Instance matchData - see below Customizing Matching by Other Mechanisms for details.
  3. You can change the Pending Match matchData - see below Working with Custom Scripts for details.
  4. Even if you change the Pending Match matchData or the Match Instance matchData, you will have to synchronize the data manually after this point.

Example 2

This second example builds on example 1 but introduces players with different skill levels:

1. Suppose we have 4 players, who each request a match while playing your game. The Match configuration you have built into the game imposes these matching constraints:

2. Now, suppose players 1, 2, 3 , and 4 issue their requests in this sequence:

Note: A Pending Match and a Match Instance are entirely separate collections in the Mongo database.

How does manual matching affect the matchmaking process?

If you select the manual matchmaking option, Pending Matches will be created automatically in the normal way but the automatic joining of Pending Matches as the matchmaking process plays out is disabled. You can then use manual matching to control which of the PMs are joined together. When a Pending Match that you have made manually meets all of the other matching criteria, a Match Instance will be formed. See the following section on Manual Matching.

Creating a Match

1. To create a Match, in the portal navigate to Configurator > Matches. The Matches page opens.

2. Click to Add a new Match. The page adjusts to allow you to enter the details for the new Match:

3. On the Thresholds panel, click to Add the Thresholds you want to use in the Match.

In this example:

Allowing Single-Player Matches? You must set the minimum number of players for a Match to be at least 1. When you set Min Players at 1, this means a single player in your game can initiate and declare a Match and await other players to join the Match, which is especially useful for Real-time Matches.

Head-to-Head Match? If you want to set up a Head-to-Head Match, then set both Min Players and Max Players at 2.

In this example, we haven't:

4. Click to Save and Close the Match. You are returned to the Matches tab and your Match is listed:

Working with Thresholds

You can use the Thresholds that you add to a Match configuration to define the criteria that determine player matching. For each Threshold you can define:

At Least One! You must add at least one Threshold to the Match. If you do not have at least one Threshold in the Match, when you try to matchmake from the client you will get this error: "matchShortCode: NOT_FOUND".

Example. Here, we'll use the Thresholds defined for our example Match configuration to explain how you can use Threshold Types and Periods, and how to use Accept Minimum Players for a Threshold:

Threshold Types

A Match is built upon the different types of range criteria for the common attribute (that is, skill level) that we want to match players on. You can use 3 different types of range criteria for Thresholds to fine-tune your Matches and achieve more precision when matching players with similar attributes:

Threshold Periods

You can use the Period value for a Threshold in your Match to specify how long you want to look for a match based on that Threshold's criteria.

We saw in the example that we can create multiple Threshold periods during one Match and for those Periods of time specify different Threshold types on which players will be matched - we had 3 Threshold Types, all with different Periods. Once the MatchmakingRequest is executed, if a match is not found in the first period, the following periods will continue subsequently to try to find a match until their duration has expired. By having a combination of longer and shorter Periods for the Thresholds, we can fine-tune our Match criteria to be stricter or more relaxed, while the duration of the Match progresses.

Our example showed how the use of 3 Thresholds types allows you to control the matching process performed by the portal such that you get a decreasing degree of matching precision as the matching process runs through the 30 second total matching period:

Custom Scripts? Note that the Threshold period is unaffected by the time taken for any Custom Scripts you've configured for your Match to execute. See the following Working with Custom Scripts section.

Accept Minimum Players

Selecting this for one of the Thresholds in your Match instructs the Match to match all the players it has currently found, as soon as the number of players found and matched is equal to the value you've entered in the Min Players field. If a sufficient number of players according to this value have not been found, the Match continues to find players in the next Threshold.

Example: Suppose our first Threshold, using Absolute type, was selected to Accept Min Players. For our MULTI_MCH configuration, Min Players is set at 2. Now suppose a player with skill level 20 issues a MatchmakingRequest using MULTI_MCH. If 1 other matching player is found in the first 10 seconds - at, say, 4 seconds in with skill level 21 - then the Match would be made for these 2 players and the matchmaking process would cease after only 4 seconds.

Accept Min. Players Only Applied to One Threshold

Important! You can only apply Accept Min. Players to one Threshold in a Match.

However, where there are multiple Thresholds there are two important things to note:

Remember! If Accept Min. Players is not selected for any Threshold, and there aren't enough players found in the Match to reach the Max Players value, no Match will be found, even if there are more players than the Min Players value.

Multiplayer Matching Examples

Using the three Thresholds in the above MULTI_MCH example and for a 3-player context with Accept Min. Players enabled for the 2nd Threshold, here are some example scenarios to demonstrate just how the matching process works:

Multiplayer Matching in the Test Harness

To match multiple Players, we will use the MatchmakingRequest in the Test Harness.

1. First, for this example, we'll change the Accept Min. Players and apply it to the 3rd Threshold instead:

2. In the Test Harness, authenticate 4 Players (in separate browser tabs) and have them each submit a MatchmakingRequest within 10 seconds so that all Players can attempt to match each other in their first threshold period.

Players 1, 2, 3 and 4 should have a skill of 16, 21, 19 and 22 respectively.

{
 "@class": ".MatchmakingRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "skill": 16
}
{
 "@class": ".MatchmakingRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "skill": 21
}
{
 "@class": ".MatchmakingRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "skill": 19
}
{
 "@class": ".MatchmakingRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "skill": 22
}

As you will notice, Players 2 and 3 will be matched almost immediately, because their skill values fall within the first (Absolute) threshold period.

Player 3 MatchUpdatedMessage:


{
  "@class": ".MatchUpdatedMessage",
  "addedPlayers": [
    "5a5f616d7eebc0609926bbff"
  ],
  "gameId": 358719,
  "matchData": {},
  "matchGroup": "group1",
  "matchShortCode": "MULTI_MCH",
  "messageId": "5a5f64df7eebc0493fde9f26",
  "notification": true,
  "participants": [
    {
      "displayName": "MATCH PLAYER 2",
      "externalIds": {},
      "id": "5a5f61537eebc0609926bbab",
      "online": true
    },
    {
      "displayName": "MATCH PLAYER 3",
      "externalIds": {},
      "id": "5a5f616d7eebc0609926bbff",
      "online": true
    }
  ],
  "playerId": "5a5f616d7eebc0609926bbff",
  "summary": "MatchUpdatedMessage"
}



Around 10 seconds later, Player 4 will also be added to the Match, who will be matched based on his skill value falling within the second (Relative) threshold period. As each Player is added to the Match, both the new and the existing Players in the Match receive a MatchUpdatedMessage.

Player 3 MatchUpdatedMessage:


{
  "@class": ".MatchUpdatedMessage",
  "addedPlayers": [
    "5a5f61857eebc0609926beff"
  ],
  "gameId": 358719,
  "matchData": {},
  "matchGroup": "group1",
  "matchShortCode": "MULTI_MCH",
  "messageId": "5a5f64e67eebc0493fde9f4d",
  "notification": true,
  "participants": [
    {
      "displayName": "MATCH PLAYER 2",
      "externalIds": {},
      "id": "5a5f61537eebc0609926bbab",
      "online": true
    },
    {
      "displayName": "MATCH PLAYER 3",
      "externalIds": {},
      "id": "5a5f616d7eebc0609926bbff",
      "online": true
    },
    {
      "displayName": "MATCH PLAYER 4",
      "externalIds": {},
      "id": "5a5f61857eebc0609926beff",
      "online": true
    }
  ],
  "playerId": "5a5f616d7eebc0609926bbff",
  "summary": "MatchUpdatedMessage"
}


Finally, Player 1 will be added to the Match, who is matched based on his skill value falling within the third (Percent) threshold period. This will result in a MatchFoundMessage.



{
  "@class": ".MatchFoundMessage",
    "gameId": 358719,
    "matchData": {},
    "matchGroup": "group1",
    "matchId": "5a5f64f07eebc0493fde9fe0",
    "matchShortCode": "MULTI_MCH",
    "messageId": "5a5f64f07eebc0493fde9ff3",
    "notification": true,
    "participants": [
      {
        "displayName": "MATCH PLAYER 2",
        "externalIds": {},
        "id": "5a5f61537eebc0609926bbab",
        "online": true,
        "peerId": 2
      },
      {
        "displayName": "MATCH PLAYER 1",
        "externalIds": {},
        "id": "5a5f613d7eebc0609926b79d",
        "online": true,
        "peerId": 1
      },
      {
        "displayName": "MATCH PLAYER 3",
        "externalIds": {},
        "id": "5a5f616d7eebc0609926bbff",
        "online": true,
        "peerId": 3
      },
      {
        "displayName": "MATCH PLAYER 4",
        "externalIds": {},
        "id": "5a5f61857eebc0609926beff",
        "online": true,
        "peerId": 4
      }
    ],
    "pendingMatchData": {},
    "playerId": "5a5f616d7eebc0609926bbff",
    "summary": "MatchFoundMessage"
}

pendingMatchData? Note that in this example there are no pendingMatchData so nothing has been copied over to the Match instance.

3. Repeat Step 2, but instead, change the skill value for Player 1 to be 12. As Players 2, 3 and 4 are added to the Match, the average skill value in the Match increases and Player 1's skill value will be too far away from the average value in the Match, even during the third (Percent) threshold period to match them. A MatchNotFoundMessage message will be returned to Player 1, whilst Players 2, 3 and 4 will receive a MatchFoundMessage because the 3rd and last threshold will accept 3 players.

{
  "@class": ".MatchNotFoundMessage",
  "gameId": 358719,
  "matchGroup": "group1",
  "matchShortCode": "MULTI_MCH",
  "messageId": "5a5f65c17eebc0493fdeb273",
  "notification": true,
  "playerId": "5a5f613d7eebc0609926b79d",
  "summary": "MatchNotFoundMessage"
}

As a result, no Match will be created. The Match we created earlier can be viewed using the MatchDetailsRequest.

Here is the request submitted by Player 4:

{
  "@class": ".MatchDetailsRequest",
 "matchId": "5a5f64f07eebc0493fde9fe0",
 "realtimeEnabled": false
}


And the MatchDetailsResponse:

{
  "@class": ".MatchDetailsResponse",
  "matchData": {},
  "matchId": "5a5f64f07eebc0493fde9fe0",
  "opponents": [
    {
      "displayName": "MATCH PLAYER 2",
      "externalIds": {},
      "id": "5a5f61537eebc0609926bbab",
      "online": true,
      "peerId": 2
    },
    {
      "displayName": "MATCH PLAYER 1",
      "externalIds": {},
      "id": "5a5f613d7eebc0609926b79d",
      "online": true,
      "peerId": 1
    },
    {
      "displayName": "MATCH PLAYER 3",
      "externalIds": {},
      "id": "5a5f616d7eebc0609926bbff",
      "online": true,
      "peerId": 3
    }
  ],
  "peerId": 4,
  "playerId": "5a5f61857eebc0609926beff"
}


Cancelling a Match Request

4. To cancel an ongoing MatchmakingRequest, use the action parameter in the request with the value, cancel.

{
 "@class": ".MatchmakingRequest",
 "action": "cancel",
 "matchShortCode": "MULTI_MCH"
}

This will cancel a MatchmakingRequest that has not yet received a MatchFoundMessage.

Using Drop In Drop Out

In this type of Match, a Match is made in the normal way but the player list found for the Match doesn't remain fixed after the Match is made. Players that meet all of the matching criteria can enter or leave the Match.

Two constraints are imposed:

Note: The specified minimum number of players is applied for making the Match in the first place. However, after the Match has been made and during the Drop In/Drop Out period, player numbers can fall below the minimum and the Match will not be ended. This allows more players to drop in again and bring the number in the Match back up above the minimum.

There are two important settings for this type of Match:

Here, we've edited our earlier multiplayer Match example and enabled Drop In/Drop Out:

Change MatchInstance Blocked! When you enable Drop In/Out for a Match, any change in the MatchInstance, such as addPlayers or addPlayersById, is blocked because this would have unpredictable consequences for the Drop In/Out matchmaking process. If you attempt to change the MatchInstance, you'll receive an error.

Manually Matching

You might want the GameSparks portal to process all the matching criteria you have built into a Match configuration, produce a list of players that meet those criteria, but not have the portal complete the Match in the normal way. Instead, you want to use your own custom mechanism to complete the Match and based on the player list found for the Match. You can use Manual Matching for this sort of case and you must select to Manually match players:

To build a custom completion mechanism for a Match, you will typically use FindPendingMatchesRequest and JoinPendingMatchRequest.

Possible Availability Lag! If you use manual matching, an "availability lag" might occur and prevent the Match being made. This would happen in cases where the matching process has presented the results for players meeting the matching criteria, but by the time your custom mechanism actually completes the Match one or more of the matched players is no longer available and the Match will not go through.

Manually Matching Players in the Test Harness

To manually match players, we'll use FindPendingMatchesRequest and JoinPendingMatchRequest in the Test Harness.

As an example, we'll use the Match configuration described in the previous section - MULTI_MCH:

For manual matching, we'll adapt scenario 1:

Players 1 and 3 will NOT be matched automatically BUT we can use FindPendingMatchRequest to find out who could potentially be matched, which allows you to decide who you want to match manually.

1. Player 1 submits a FindPendingMatchesRequest at 12 seconds:


{
 "@class": ".FindPendingMatchesRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "maxMatchesToFind": 10
}

Note: Here, we've arbitrarily set the maximum numbers of Pending Matches we want returned at 10.

2. Player 1 receives the response:


{
 "@class": ".FindPendingMatchesResponse",
 "pendingMatches": [
  {
   "id": "579756b33a32df04880bc2d9",
   "matchGroup": "group1",
   "matchShortCode": "MULTI_MCH",
   "matchedPlayers": [
    {
     "location": {
      "type": "Point",
      "coordinates": [
       -1.08270263671875,
       53.95759582519531
      ]
     },
     "playerId": "5792410f6cc8e27fffe92509",
     "skill": 17
    }
   ],
   "skill": 17
  }
 ]
}


This tells us that there is an available Pending Match at this point for player 3 (by the "playId" and "skill" returned).

If player 3 were to have submitted the FindPendingMatchesRequest at 12 seconds he'd get a similar FindPendingMatchesResponse but showing an available Pending Match for player 1.

3. However, if player 3 submitted a FindPendingMatchesRequest at 22 secs - that is, into the 3rd Threshold period for Percent skill-level matching - the response would show no possible Pending Matches, that is, empty:


{
 "@class": ".FindPendingMatchesResponse"
}


This does not mean that there are NO Pending Matches at all, but, simply and correctly, that none of the available Pending Matches at that point in the matchmaking process are suitable for matching with player 3.

4. If player 3 submitted a FindPendingMatchesRequest but hadn't previously submitted a MatchmakingRequest, the FindPendingMatchResponse would show an error:


{
 "@class": ".FindPendingMatchesResponse",
 "error": {
  "match": "NOT_IN_PROGRESS"
 }
}

This makes perfect sense - a player can't be put into any Pending Match unless they've first issued a MatchmakingRequest!

5. Lastly, player 1, having seen the possible Pending Match returned at 12 seconds, submits a JoinPendingMatchRequest at 18 seconds - that is, still within the second Threshold period for relative skill-level matching - and uses the returned Pending Match id for this request:


{
 "@class": ".JoinPendingMatchRequest",
 "matchGroup": "group1",
 "matchShortCode": "MULTI_MCH",
 "pendingMatchId": "579756b33a32df04880bc2d9"
}


Because the first Threshold in the Match configuration is selected to Accept Min. Players (that, is at least 2) when player 1 joins the Pending Match, a Match Instance is created and the player receives a MatchFoundMessage:


{
 "@class": ".MatchFoundMessage",
 "gameId": 358719,
 "matchData": {},
 "matchGroup": "group1",
 "matchId": "579756fb3a32df04880bc49f",
 "matchShortCode": "MULTI_MCH",
 "messageId": "579756fb3a32df04880bc4aa",
 "notification": true,
 "participants": [
  {
   "displayName": "PLAYER_THREE",
   "externalIds": {},
   "id": "5797427a3a32df04880b2e56",
   "online": true,
   "peerId": 2
  },
  {
   "displayName": "PLAYER_ONE",
   "externalIds": {},
   "id": "579742353a32df04880b2c15",
   "online": true,
   "peerId": 1
  }
 ],
 "pendingMatchData": {},
 "playerId": "579742353a32df04880b2c15",
 "summary": "MatchFoundMessage"
}


And then receives a JoinPendingMatchResponse:


{
 "@class": ".JoinPendingMatchResponse",
 "pendingMatch": {
  "id": "579756af3a32df04880bc2d3",
  "matchGroup": "group1",
  "matchShortCode": "MULTI_MCH",
  "matchedPlayers": [
   {
    "location": {
     "type": "Point",
     "coordinates": [
      -1.08270263671875,
      53.95759582519531
     ]
    },
    "playerId": "579742353a32df04880b2c15",
    "skill": 20
   },
   {
    "location": {
     "type": "Point",
     "coordinates": [
      -1.08270263671875,
      53.95759582519531
     ]
    },
    "playerId": "5797427a3a32df04880b2e56",
    "skill": 17
   }
  ],
  "skill": 18.5
 }
}

Working with Custom Scripts

You can use a Custom Script to fine-tune the matchmaking process for when a player joins or leaves a Pending Match and ensure the found Matches precisely serve your game's Matchmaking requirements:

Team Matchmaking? Adding a Custom Script to your Match configuration facilitates in a dependable way the team matchmaking use case - for example, where you want the found Match to result in a 6v6 team Match. For a detailed example of how to use a script for Team matchmaking, see the Team Matchmaking tutorial in this section.

Other Match Customization? Using a Custom Script is intended specifically for customizing how Pending Matches behave when a player joins or leaves a Match. For other ways of customizing Matchmaking for your game, see the following Customizing Matching section.

Writing Pending Match Custom Scripts

When you build a Pending Match script in the Custom Script panel, you are restricted to a fixed number of methods. For a full list of these methods, see Appendix 1 below.

Customizing Matching by Other Mechanisms

There are other mechanisms you can use to introduce further matching customization into your game.

Participant Data and Match Data

Other types of data can be added for matchmaking:

Custom Query

You can use Custom Query when you want more complex rules for grouping Pending Matches. Each participant in a Pending Match can have a Custom Query, which is specific to them as a player seeking a match, that can be run against a different Pending Match to see if the two Pending Matches can be joined. In this way, you can use a Custom Query to impose tight conditions on when Pending Matches can be joined.

A simple example of using this capability would be for players who only want to be matched with people playing in their own country. To do this, each MatchmakingRequest will contain Participant Data and a Custom Query to prevent the requesting player from being put into a Pending Match where there are players from another country.

Using Custom Query? You can use the customQuery request parameter for a MatchmakingRequest.

Appendix 1 Pending Match Custom Script Methods

This section lists the methods available to you when creating a script for Pending Match in the Custom Script panel. There are two important things to note at the outset:


UpdatingPendingMatch


setMatchData

signature setMatchData()

returns void

Sets the matchData for the updating pending match.

example

pendingMatch.setMatchData(matchData);


getMatchData

signature getMatchData()

returns JSON

Returns the matchData for the updating pending match.

example

pendingMatch.getMatchData();


getPlayersDetails

signature getPlayersDetails()

returns UpdatingMatchPlayerDetails[]

The players already part of this pending match.

example

pendingMatch.getPlayersDetails();


UpdatingMatchPlayerDetails


setCustomQuery

signature setCustomQuery(customQuery: ?)

returns void

Sets the query associated with this user that will be applied to the PendingMatch collection.

example

var playerId = pendingMatch.getPlayersDetails()[0].setCustomQuery({"teamA":{"$lt":5}});


getCustomQuery

signature getCustomQuery(?)

returns JSON

Gets the query associated with this user that will be applied to the PendingMatch collection.

example

var playerId = pendingMatch.getPlayersDetails()[0].getCustomQuery();


getLocation

signature getLocation(?)

returns JSON

Gets the location of the player.

example

var playerId = pendingMatch.getPlayersDetails()[0].getLocation();


getParticipantData

signature getParticipantData(?)

returns JSON

A JSON Map of any data that was associated to this user.

example

var playerId = pendingMatch.getPlayersDetails()[0].getParticipantData();


getPlayerId

signature getPlayerId()

returns string

Gets the GameSparks ID of the matched player.

example

var playerId = pendingMatch.getPlayersDetails()[0].getPlayerId();


getSkill

signature getSkill()

returns number

Gets the skill of the player.

example

var playerId = pendingMatch.getPlayersDetails()[0].getSkill();