AWS for Games Blog

Fine-tune online game matchmaking with Amazon GameLift FlexMatch rule sets and the Amazon GameLift Testing Toolkit

Creating matches in online games that are fair and fun for everyone is a key part of a great gaming experience. However, as the number of players being matched increases, the number of potential matches increases exponentially, making the task of creating balanced matches more difficult. One way to fine tune matchmaking to deliver high-quality matches and create great multiplayer experiences for hundreds of players is Amazon GameLift FlexMatch. This service handles the logic of matchmaking players while giving you, the developer, a way to adjust which matches are created through a rules-based syntax called FlexMatch rule sets. You can use FlexMatch as a standalone matchmaking service or integrated with Amazon GameLift fully managed game server hosting.

In this post, we’ll explore FlexMatch rule sets—the different rule types and property expressions used to compare different player attributes. You can use the values these expressions produce to fine-tune and implement matchmaking for a wide variety of game types.

We’ll also show you how to test these rule sets by simulating matchmaking using the Amazon GameLift Testing Toolkit. This toolkit helps game developers to optimize their multiplayer games by providing quick ways to test, simulate, and visualize the resources in Amazon GameLift and how players move between them. To learn more about what you can do with the GameLift Testing Toolkit, see the Amazon GameLift Testing Toolkit documentation.

Prerequisites and setup

First, we’ll download and follow the instructions in the Amazon GameLift Testing Toolkit Repository. The prerequisites listed in this repository are the same as the ones you’ll need to follow along with this blog post. After you follow the instructions, navigate to the resulting CloudFront URL and sign in, you will see a page that looks like this:

Amazon GameLift Testing Toolkit landing page.

To test FlexMatch, you will need to create:

  1. A pool of player profiles to simulate matchmaking between
  2. The rule sets we want to test

Players, attributes, and player profiles

First, we’ll need to understand players and their attributes. For FlexMatch and the GameLift Testing Toolkit, players have a list of attributes. An attribute is one of four data types, including string, number, a list of strings, or a string number map (a list of key-value pairs, where each value is a double). Examples of potential attributes include things like a skill level or rating, preferred character or position, or a list of players that the player doesn’t want to play with.

In a real game deployment, players and their data (including attributes) might be stored in a backend database like Amazon DynamoDB, and you would pass the player IDs and their attributes to FlexMatch. FlexMatch could then create and enforce rules that refer to these attributes in a rule set. For example, if you send a list of players with a number attribute “skill” to FlexMatch for matchmaking, you can use a rule to make sure that the players matched together are all numerically close in skill.

In the GameLift Testing Toolkit, you create player profiles by choosing the cog in the top left and choosing Player Profiles. When you choose Create Player Profile on this screen, there will be a form where you give the player profile a name and add any number of attributes to it. You can also edit existing profiles using the Edit Profile button.

For this blog post, we’ll create some player profiles and add an attribute with an attribute name of “skill,” an attribute type of “Number” with Value Type “Value.” Make the skill values within 10, 50, and 100 of each other. For ease of reading matchmaking results, we’ll give the player profiles names that show what their “skill” attribute is (such as, player10, player20, player100, etc.). Later, we’ll use the FlexMatch Simulator to simulate matchmaking with these players.

To save time when creating many profiles, the GameLift Testing Toolkit allows you to create player profiles where each player with that profile has random attributes in a range of numbers or from a list of strings that you specify. For example, you could make a profile called “higherSkillPlayers” with players that have a skill attribute ranging from 100 – 200, and another profile called “lowerSkillPlayers” with players that have a skill attribute ranging from 0 – 50. To do this, change the “Value Type” of your attribute to Random Integer, Random Double, or Random List and specify the random values for the attributes.

Rule Sets

When using FlexMatch, you create a rule set, or a JSON file, which determines the two key elements of a match that you define: your game’s team structure and size, and how to group players together for high-quality matches through a list of rules.

Specify the following attributes of a rule set:

  • Name
  • The underlying algorithm that the rule set uses to create batches of players to matchmake
  • Team sizes
  • Number of teams
  • The player attributes you want the rule set to use
  • The rules themselves
  • Any expansions to the rules for when you want to make rules less strict if matchmaking is taking too long.

For more information about all possible properties and allowed values of a rule set, see FlexMatch rule set schema.

Now, we’ll go through an example rule set. We’ll break the rule set into annotated blocks, then put it together.

First, we’ll name our rule set. We’ll also specify the rule language version (currently 1.0) to use. Next, we’ll also declare our player attributes, listing all the player attributes that we want to pass to FlexMatch in a section called “playerAttributes”. For each attribute listed in the “playerAttributes” property, there should be an attribute with the same name and type attached to players in the backend player data store or to the Player Profiles in the GameLift Testing Toolkit. Since the player profiles we created only have one attribute “skill,” that will be the only attribute that we include in the following example code:

{
    "name": "aliens_vs_cowboys",
    "ruleLanguageVersion": "1.0",
    "playerAttributes": [{ 
        "name": "skill",
        "type": "number",
        "default": 10
    }],

Next, we’ll define our match teams by creating a list of them in the “teams” section. For each team, we’ll need to specify its name, and the maximum and minimum number of players that can be on the team. FlexMatch will initially match the minimum number of players in a team, unless there’s a sufficient volume of matchmaking tickets to fully fill both teams. You can then add more players through the automatic and manual backfill features of FlexMatch. We specify our teams in the following example code:

teams": [{ 
    "name": "cowboys",
    "maxPlayers": 8,
    "minPlayers": 4
}, {
    "name": "aliens",
    "maxPlayers": 8,
    "minPlayers": 4
}],

Now, we’ll create our list of rules. To create rules that enforce what we want, we need to understand property expressions. Property expressions are the calculations and logic that you can apply to player attributes to get a value that you can use as a reference in rules. To view all possible FlexMatch property expressions and their specifications, see FlexMatch property expressions. For example, if you want to get the average rank of players in a potential match, you would use the following property expression:

avg(flatten(teams[*].players.attributes[skill])

teams[*].players.attributes[skill] is a nested list, or a list of lists, that contains, for each team, a list of player skills (see the following example).

teams[*].players.attributes[skill]: [[50, 60, 70], [30, 90, 60], [60, 60, 60]]

flatten denests the list, or turns the nested list into one list of players’ skills (see the following example).

flatten(teams[*].players.attributes[skill]): [50, 60, 70, 30, 90, 60, 60, 60, 60]

Finally, the avg expression takes the average of all numbers on the list (see the following example).

avg(flatten(teams[*].players.attributes[skill])): 60

Now we use the value produced by this average skill expression to create the following code example: a distance rule to ensure that no player’s skill is too far from the average skill of the match:

"rules": [{
    "name": "FairTeamSkill",
    "description": "The average skill of players in each team is within 10 points from the average skill of all players in the match",
    "type": "distance", // rule type; this rule type enforces a numerical distance from one value to a reference value
    // get skill values for players in each team and average separately to produce list of two numbers
    "measurements": [ "avg(teams[*].players.attributes[skill])" ],
    // get skill values for players in each team, flatten into a single list, and average to produce an overall average
    "referenceValue": "avg(flatten(teams[*].players.attributes[skill]))",
    "maxDistance": 10 // the numerical distance between the average skill of each team (measurements) and the average skill of the match (referenceValue) must be 10 or less
},

The rule we created has a property called “type.” There are 8 different rule types, each with different uses and additional properties that must be set. For the full list of rule types, their properties, and their syntax with examples, see FlexMatch rule types. Now that we understand rules more, let’s add another rule to ensure teams have equal players using a comparison rule:

{
    "name": "EqualTeamSizes",
    "description": "Only launch a game when the number of players in each team matches, such as 4v4, 5v5, 6v6, 7v7, 8v8",
    "type": "comparison",
    "measurements": [ "count(teams[cowboys].players)" ],
    "referenceValue": "count(teams[aliens].players)",
    "operation": "=" // other operations: !=, <, <=, >, >=
}],

Expansions are an optional ruleset property that allow you to target an existing rule and relax its requirements over time. When matchmaking is taking too long because your rules are too strict, you can use expansions to make them less strict. The following code applies an expansion to our FairTeamSkill distance rule:

"expansions": [{ 
    "target": "rules[FairTeamSkill].maxDistance", // which rule you want to relax, in this case the one named "FairTeamSkill"
    "steps": [{// each step defines which at which interval to expand the rule criteria and to which value
        "waitTimeSeconds": 5, // expand the rule criteria at 5 seconds
        "value": 50 // expand the maximum distance allowed between average team skill and average player skill from 10 to 50
    }, {
        "waitTimeSeconds": 15, // expand the rule criteria again at 15 seconds
        "value": 100 // expand the maximum distance allowed between average team skill and average player skill from 50 to 100
    }]

Finally, we can put everything together to create the whole rule set. Note that we’ve deleted the comments in the annotated blocks, as comments are not syntactically supported in FlexMatch rule sets. The following code is the full rule set:

{
    "name": "aliens_vs_cowboys",
    "ruleLanguageVersion": "1.0",
    "playerAttributes": [{
        "type": "number",
        "default": 10
    }],
    "teams": [{
        "name": "cowboys",
        "maxPlayers": 8,
        "minPlayers": 4
    }, {
        "name": "aliens",
        "maxPlayers": 8,
        "minPlayers": 4
    }],
    "rules": [{
        "name": "FairTeamSkill",
        "description": "The average skill of players in each team is within 10 points from the average skill of all players in the match",
        "type": "distance", 
        "measurements": [ "avg(teams[*].players.attributes[skill])" ],
        "referenceValue": "avg(flatten(teams[*].players.attributes[skill]))",
        "maxDistance": 10
    }, {
        "name": "EqualTeamSizes",
        "description": "Only launch a game when the number of players in each team matches, such as 4v4, 5v5, 6v6, 7v7, 8v8",
        "type": "comparison",
        "measurements": [ "count(teams[cowboys].players)" ],
        "referenceValue": "count(teams[aliens].players)",
        "operation": "="
    }],
    "expansions": [{
        "target": "rules[FairTeamSkill].maxDistance",
        "steps": [{
            "waitTimeSeconds": 5,
            "value": 50
        }, {
            "waitTimeSeconds": 15,
            "value": 100
        }]
    }]
}

Simulating matchmaking using the GameLift Testing Toolkit

Now we’ll go through using the GameLift Testing Toolkit to simulate matchmaking between our pool of players. On the sidebar, choose Manage Rule Sets.

Amazon GameLift Testing Toolkit Manage Rule Sets menu.

Choose New Rule Set, then copy and paste the rule set from above.

Amazon GameLift Testing Toolkit Rule Set creation page.

Now, we can choose Simulate Matchmaking, select the rule set we wish to simulate, and then add player profiles to function as the pool of players. You’ll notice options to add a latency profile, modify the number of players with each player profile, and to modify the time at which player profiles begin matchmaking/enter into the matchmaking pool. Latency profiles are another tool to help you easily test latency rules. Refer to the workshop linked in the Cleaning up section of this post, which walks through using latency profiles to test latency rules. Modifying the time at which player profiles start matching allows you to more conveniently test expansion rules. You can start the matching at the time intervals which you specified for expansion rules to take effect to test them without having to wait for the actual duration.

Amazon GameLift Testing Toolkit Simulate Matchmaking menu.

Choose Run Simulation and the simulation will begin. Once complete, you can view the results of the simulation by choosing Show Simulation Results. On this table, you’ll see the number of each player profile placed on each team.

We can see that FlexMatch has created two matches, each with teams of similar average skill, from our pool of 16 players with varying skill levels. Choosing Match Info on each of the items in the table will show you more details about who was placed together in a match.

Amazon GameLift Testing Toolkit Simulate Matchmaking results page.

Clean up

Before you shut down the resources that were spun up in this post, we encourage you to look at the Amazon GameLift Testing Toolkit Workshop, which includes a section on FlexMatch simulation as well as other ways you can use the toolkit. Once you are done, you can deprovision resources that the GameLift Testing Toolkit spun up. See the Removing the toolkit instructions.

Conclusion

In this post, we showed how FlexMatch rule sets give you many ways to fine-tune matchmaking to create fun and balanced matches. FlexMatch lets you focus on setting matchmaking priorities to create high-quality matches while FlexMatch handles the matchmaking algorithms. You can use your player data and FlexMatch rule expressions to generate the values that you need for the rule types and rules you want to enforce in your players’ matches.

With the versatility of FlexMatch rule sets, we encourage you to continue exploring what you can do with FlexMatch rule sets using the FlexMatch rule set documentation, the Amazon GameLift Testing Toolkit Workshop, and the other see FlexMatch rule set examples that AWS provides.