Skip to main content

Ruleset Reference

1. Overview

This document contains two key aspects:

  1. A Ruleset Example - Helps you concisely understand the syntax and core components of a Ruleset through practical demonstration.
  2. Ruleset Schema Specifications - Provides detailed technical explanations of the syntax rules, structural elements, and implementation requirements.

2. Ruleset Example

Here is a sample ruleset with detailed comments.

  • It demonstrates two player attributes:
    • skill: suggests the skill points of a player, which is defined on PGOS Player Data and its value will be automatically retrieved by PGOS backend.
    • mode: suggests the game mode selected by player before entering matchmaking, which is defined in the ruleset and its value should be passed by game client through PGOS SDK.
// The section of playerAttributes describes the player attributes used in the matching rules
"playerAttributes": [
{
"name": "skill", // The skill value of a player, the most common case is calculated by ELO algorithm
"key": "skill", // With this key, this attribute will be automatically retrieved from Player KVData in which key is "skill"
"type": "number", // Data type of attribute, can be "number" or "string"
"default": 0,
"partyAggregation": "avg" // Use the avg value for multiplayer matchmaking ticket
},
{
"name": "mode", // The game mode such as PvP or PvE, which must be submited from game client
"type": "string", // Data type of attribute, can be "number" or "string"
"default": "solo"
}
],
  • It defines two teams:
    • red: 4~8 players.
    • blue: 4~8 players.
// The section of "teams" describes the team structure of the game
// The matchmaking system will try to gather as many players as the maxPlayers indicates. And it will compromise to minPlayers if there are not enough players in matchmaking pool.
"teams": [
{
"name": "red", // The size of red team is 4~8 players
"maxPlayers": 8,
"minPlayers": 4
},
{
"name": "blue", // The size of blue team is 4~8 players
"maxPlayers": 8,
"minPlayers": 4
}
],
  • It has four rules:
    • skillRule: makes the skill value of players in a battle as close as possible.
    • modeRule: guarantees that mode value players selected must be the same.
    • countRule1: guarantees that both red and blue team has equal number of players.
    • countRule2: effects the same as countRule1, but demonstrates another way to configure.
// You can add multiple rule descriptions to the section of "rules", and PGOS searches for players based on these rules
"rules": [
{
// Makes the skill value of players in a battle as close as possible.
"name": "skillRule",
"type": "distanceRule",
"measurements": [
"flatten(teams[*].players.playerAttributes[skill])"
],
"referenceValue": "avg(flatten(teams[*].players.playerAttributes[skill]))",
"maxDistance": 50
},
{
// Guarantees that mode value players selected must be the same.
"name": "modeRule",
"type": "comparisonRule",
"measurements": [
"flatten(teams[*].players.playerAttributes[mode])"
],
"operation": "="
},
{
// Guarantees that both red and blue team has equal number of players.
"name": "countRule1",
"type": "comparisonRule",
"measurements": [
"count(teams[*].players)"
],
"operation": "="
},
{
// Effects the same as countRule1, but demonstrates another way to configure.
"name": "countRule2",
"type": "comparisonRule",
"measurements": [
"count(teams[blue].players)"
],
"referenceValue": "count(teams[red].players)",
"operation": "="
}
],
  • It has two expansions:
    • If no matches are found in 5 seconds, the maxDistance of skillRule will be expanded to 100, and 10 seconds for 200.
    • If no matches are found in 5 seconds, the minimum team size will be decreased to 2 players, and 10 seconds for just 1 player.
// The section of "expansions" is "rules" expansions
// The meaning of the first expansion is:
// (1). Wait for 5s, if thereis no matchmaking result, then modify rules[skillRule].maxDistance to 100,
// modify rules[distanceRuleExample].maxDistance to 15
// (2). Wait for 10s,if thereis no matchmaking result,then modify rules[skillRule].maxDistance to 200,
"expansions": [
{
// If no matches are found after 5 seconds, the maxDistance of `skillRule` is expaned to 100, and 10 seconds for 200.
"target": "rules[skillRule].maxDistance",
"steps": [
{
"waitTimeSeconds": 5,
"value": 100
},
{
"waitTimeSeconds": 10,
"value": 200
}
]
},
{
// If no matches are found after 5 seconds, the minimum team size is decreased to 2 players, and 10 seconds for just 1 player.
"target": "teams[*].minPlayers",
"steps": [
{
"waitTimeSeconds": 5,
"value": 2
},
{
"waitTimeSeconds": 10,
"value": 1
}
]
}
]

Finally, the whole complete ruleset is as follows:

{
"version": "v1.0",
"playerAttributes": [
{
"name": "skill",
"type": "number",
"default": 0
},
{
"name": "mode",
"type": "string",
"default": "solo"
}
],
"teams": [
{
"name": "red",
"maxPlayers": 8,
"minPlayers": 4
},
{
"name": "blue",
"maxPlayers": 8,
"minPlayers": 4
}
],
"rules": [
{
"name": "skillRule",
"type": "distanceRule",
"measurements": [
"flatten(teams[*].players.playerAttributes[skill])"
],
"referenceValue": "avg(flatten(teams[*].players.playerAttributes[skill]))",
"maxDistance": 50
},
{
"name": "modeRule",
"type": "comparisonRule",
"measurements": [
"flatten(teams[*].players.playerAttributes[mode])"
],
"operation": "="
},
{
"name": "countRule1",
"type": "comparisonRule",
"measurements": [
"count(teams[*].players)"
],
"operation": "="
},
{
"name": "countRule2",
"type": "comparisonRule",
"measurements": [
"count(teams[blue].players)"
],
"referenceValue": "count(teams[red].players)",
"operation": "="
}
],
"expansions": [
{
"target": "rules[skillRule].maxDistance",
"steps": [
{
"waitTimeSeconds": 5,
"value": 100
},
{
"waitTimeSeconds": 10,
"value": 200
}
]
},
{
"target": "teams[*].minPlayers",
"steps": [
{
"waitTimeSeconds": 5,
"value": 2
},
{
"waitTimeSeconds": 10,
"value": 1
}
]
}
]
}

3. Ruleset Schema

3.1 Overview of the ruleset

PGOS supports matchmaking ruleset descriptions with JSON script.

Structure of ruleset script

FieldDescription
versionVersion of ruleset script, must be "v1.0"
playerAttributesPlayer attributes that can be used in rules
teamsTeam definitions which can be used in rules
rulesMultiple matchmaking constraints which utilize player attributes and team data
expansionsExpansion of rules to relax the constraints

e.g

{
"playerAttributes":[
...
],
"teams":[
...
],
"rules":[
...
],
"expansions":[
...
],
}

3.2 PlayerAttributes

This contains descriptions of multiple sets of players, which allow the PGOS service to retrieve these data.

Structure of script

FieldTypeDescriptionRequiredNote
namestringName of attributeYesA string of length 32. Value range: a~z, A~Z, 0~9, _
typestringType of attributeYes"number”, “string”
defaultstring / numberDefault value of attributeNoMust match with the type of the attribute
keystringThe key defined in Player DataNoThe key of player-data in PGOS Player Data
bitmapboolWhether treat number as bitmapNoBitmap can only be true on number type attribute
partyAggregationstringThe strategy to merge attribute value of multiple players in one ticketNoDefault to "each". Possible values can be "each", "avg", "min", "max", "any", "and", "or"
Tip
  • If the key field is empty, it means that the attribute is taken from the item with the same name in the match_attributes field of the matchmaking request.
  • If the key field is not empty, it means that the data comes from the item with the same key in the Player Data.
  • All player attributes described in ruleset script will be filled to ServerPlayerDesc::match_attributes when a new battle session is produced from the ruleset. Please read this doc to learn how to handle battle session placement notification on your game server.
  • If a number attribute will be used for bit operations in following rules, set bitmap to true. In any other scenario, leave it to false.
Remark

The partyAggregation parameter is used in attribute to merge the attribute values of the player in a multiplayer request.

For example "partyAggregation" = "max" means that the attribute values of all members in a multiplayer matchmaking request are regarded as the highest one.

  • each: treat each player individually.
  • avg: combine players and select the average value of their attributes.
  • min: combine players and select the minimum value of their attributes.
  • max: combine players and select the maximum value of their attributes.
  • any: combine players and randomly select one of their attributes.
  • and: perform bitwise AND operation on the value, only available for bitmap attributes.
  • or: perform bitwise OR operation on the value, only available for bitmap attributes.

It is recommended to define partyAggregation in the player attribute. Although for backward compatibility, it can also be set in the rule, setting it in the attribute is a better approach. This facilitates PGOS in sorting match tickets based on attributes.

e.g

    "playerAttributes": [
{
"name": "age", // Attribute from match_attributes which key is "age"
"type": "number",
"default": 10,
"partyAggregation": "avg"
},
{
"name": "skill", // Attribute from match_attributes which key is "skill"
"type": "number"
},
{
"name": "kda", // Attribute from Player properties which key is "kda"
"key":"kda",
"type": "number",
"default": 10
},
{
"name": "mode", // Attribute from match_attributes which key is "mode"
"type": "number",
"bitmap": true,
"partyAggregation": "and"
}
],

3.3 Teams

Structure of script

FieldDescriptionRequiredNote
nameName of teamYesA string of length 32. Value range a~z,A~Z,0~9
minPlayersMinimum number of playersYesAn integer between 1-40
maxPlayersMaximum number of playersYesAn integer between 1-40; maxPlayers must be greater than or equal to minPlayers
minQuantityMinimum quantity of teamsNoAn integer between 1-999; default is 1
maxQuantityMaximum quantity of teamsNoAn integer between 1-999; default is 1; maxQuantity must be greater than or equal to minQuantity

The matchmaking system will try to gather as many players as the maxPlayers indicates. And it will compromise to minPlayers if there are not enough players in matchmaking pool.

Variable team quantity is an advanced feature. When matchmaking system finds a proposal that satisfy minQuantity, it will continue to fill more players to the proposal and form more teams. With sufficient players in pool, the matchmaking system will return a proposal that reaches the maxQuantity requirement.

The minPlayers and maxPlayers can also be the same, and it makes a team with fixed size. The minQuantity and maxQuantity can also be the same, and it makes fixed quantity of this team.

Tip

When maxQuantity is set >1, serial number will be joined to the team name with underscore, which starts from "001" to "999".

For example:

"teams":[
{
"name":"squad",
"minPlayers":1,
"maxPlayers":1,
"minQuantity":4,
"maxQuantity":4
}
]

Then in the matchmaking result, there will be for teams: squad_001, squad_002, squad_003, squad_004.

e.g

// 5vs5 fair play
"teams":[
{
"name":"Darkness",
"minPlayers":5,
"maxPlayers":5
},
{
"name":"Light",
"minPlayers":5,
"maxPlayers":5
}
]

// 1vs1vs1vs1vs1 solo
"teams":[
{
"name":"soloTeam",
"minPlayers":1,
"maxPlayers":1
}
]

// 4v4v4v... battle royale
"teams":[
{
"name":"squad",
"minPlayers":4,
"maxPlayers":4,
"minQuantity":10,
"maxQuantity":25
}
]
Tip

If the two features: variable players and variable team quantities are both utilized, there are actually two tendencies that the matchmaking try to match players:

  • Find as more players as maxPlayers requires
  • Find as more teams as maxQuantity requires

For example:

"teams":[
{
"name":"squad",
"minPlayers":1,
"maxPlayers":4,
"minQuantity":1,
"maxQuantity":4
}
]

Given 4 players in pool, 1v1v1v1 , 2v2, 4 as a team are all legal matchmaking results that meet the requirements above.

Currently the matchmaking system tends to meet the minQuantity and maxPlayers first, when minimum enough teams are created, it tries to raise the team quantity by one, still tries to meet maxPlayers for this team. The matchmaking continues cycling until no sufficient players in pool for the last raised team, or the whole team quantity reaches maxQuantity limit.

So it will give a 4 as a team as the final result by default. If 1v1v1v1 is wanted, the developer can utilize the "Rules Expansion" feature below to relax the minQuantity value by steps to deal with 1~4 players that are meant to be assigned each one as a team, which is demonstrated as below:

"teams":[
{
"name":"squad",
"minPlayers":1,
"maxPlayers":4,
"minQuantity":4,
"maxQuantity":4
}
],
"expansions": [
{
"target": "teams[*].minQuantity",
"steps": [
{
"waitTimeSeconds": 5,
"value": 3
}
]
},
{
"target": "teams[*].minQuantity",
"steps": [
{
"waitTimeSeconds": 10,
"value": 2
}
]
},
{
"target": "teams[*].minQuantity",
"steps": [
{
"waitTimeSeconds": 15,
"value": 1
}
]
}
]

3.4 Rules

3.4.1 Structure of script

FieldDescriptionRequiredNote
nameName of ruleYesA string of length 32. Value range a~z,A~Z,0~9,_
descriptionRule descriptionNoString of length 128
typeType of ruleYesValid values are "distanceRule", "comparisonRule", and "latencyRule"
Caution

The number of rules in a ruleset should not exceed 10.

3.4.2 Expressions of rules

Syntax Specification

The following statements shows how to identify the team and the players in the team.

// Meaning:
// Darkness team
// Example of results:
// [[Darkness]]
teams[Darkness]

// Meaning:
// All teams
// Example of results:
// [[Darkness] [Light]]
teams[*]

// Meaning:
// Players in Darkness team
// Example of results:
// [[player-1 player-2]]
teams[Darkness].players

// Meaning:
// All players group by team
// Example of results:
// [[player-1 player-2] [player-3 player-4]]
teams[*].players

The following statements shows how to get player attribute data.

// Meaning:
// Playerid of players in Darkness Team
// Example of results:
// [[player-1 player-2]]
teams[Darkness].players[playerid]

// Meaning:
// List of attribute named skill in Darkness Team
// Example of results:
// ["solo" "solo" "solo"] or [1 1 1]
teams[Darkness].players.playerAttributes[skill]

// Meaning:
// Attribute named skill of all players(Group by team)
// Example of results:
// [["solo" "solo" "solo"] ["solo" "solo" "solo"]] or [[1 1 1] [1 1 1]]
teams[*].players.playerAttributes[skill]

3.4.3 Functions of expressions

Function nameInputMeaningoutput
flattenListTurn the collection of nested lists into a single list containing all elementsList
avgListGet the average of all numbers in a listNumber
minListGet the minimum of all numbers in a listNumber
maxListGet the maximum of all numbers in a listNumber
sumListGet the sum of all numbers in a listNumber
countListGet the number of all elements in a listNumber
andListPerform bitwise AND operation on the numbers in the list.Number

Note that when avg always acts on one-dimensional list, and when the input is a nested lists, it will recurse.

Tip

Deal with nested lists

// One dimensional list condition
ListA = [[1, 2, 3]]
avg(ListA) = [2]

// Nested list condition
ListB = [[1,2,3], [3,4,5]]
avg(ListB) = [2, 4]

If you have nested lists like ListB in the case above, and you want calculate the average of all elements, you should use flatten to break nested lists into one-dimensional list first.

// Symbolic explanation
ListA = [[1, 2, 3]]

ListB = [[3, 4, 5]]

ListC = [ListA, ListB]

flatten(ListC) = [[1, 2, 3, 3, 4, 5]]

// Meaning:
// List of attribute named kda of all players
// Type of results:
// List<number>

// If
team[A].players.playerAttributes[skill] = [[1, 2, 3]]
team[B].players.playerAttributes[skill] = [[3, 4, 5]]

// Then
teams[*].players.playerAttributes[skill] = [[1,2,3], [3,4,5]]
flatten(teams[*].players.playerAttributes[skill]) = [[1, 2, 3, 3, 4, 5]]

// When combined with "avg", the difference of using "flatten" is:
avg(teams[*].players.playerAttributes[skill]) = [[2, 4]]
avg(flatten(teams[*].players.playerAttributes[skill])) = avg([1, 2, 3, 3, 4, 5]) = [3]

3.4.4 Type of Rules

Type 1. Distance rule

Property nameDescriptionRequiredValue constraint
nameName of ruleYesA string of length 32. The value range is a~z, A~Z, 0~9, _ .Unique in the same ruleset
typeType of ruleYesfixed "distanceRule"
descriptionDescription of ruleNoString of length 256
measurementsMeasured value, used to compare distance with referenceValueYesThe calculation result is an expression of List\<number> or List\<List\<number>> Support multiple number expressions with accuracy of 2 decimal places
referenceValueComparison valueYesNumber or an expression whose result is a number with accuracy of two decimal places; can be used as an expansion target
minDistanceMinimum distance differenceFill in at least one of minDistance or maxDistanceThe minimum difference of comparing each number calculated in measurements with referenceValue. The value range is a number between 0-99999 with accuracy of two decimal places. Can be used as an expansion target
maxDistanceMaximum distance differenceFill in at least one of minDistance or maxDistanceThe maximum difference of comparing each number calculated in the measurements with referenceValue, . The value range is a number between 0-99999 with accuracy of two decimal places. Can be used as an expansion target
partyAggregationHow to sort multi-player requestsNoValid options include each(each), minimum(min), maximum(max), average(avg) or any one(any) for a request's player. Default is each
// Meaning:
// The distance between average value of the "skill" of each team player and the average value of the "skill" of all players is not less than 5, not more than 10
"rules": [
{
"name": "distanceRuleExample",
"type": "distanceRule",
"measurements": [ "avg(teams[*].players.skill)" ],
"referenceValue": "avg(flatten(teams[*].players.playerAttributes[skill]))",
"minDistance":5,
"maxDistance":10
}]

Type 2. Comparison rule

Property nameDescriptionRequiredValue constraint
nameName of ruleYesA string of length 32. The value range is a~z, A~Z, 0~9, _ unique in the same ruleset
descriptionDescription of ruleNostring of length 256
typeType of ruleYesfixed "comparisonRule"
measurementsExpressions of rulesYesAn expression whose calculation result is of List<?>, which can be List\<number> or List\<string>. Only 1 expression is supported temporarily.
referenceValueValue to compare with rule expressionsNoIf referenceValue is defined, then compare each element in List\<?> with referenceValue;
If referenceValue is not defined, then compare each element in List\<?>. In this case, operation can only take the value "=" or "!="
An expression whose calculation result is the same as the element type of the measurement, such as a number or string. Can be used as an extended target
operationComparison symbolYesValid values: “=”, “!=”, “<”, “<=”, “>”, “>=” (These symbols can be used to compare numbers or strings). Can be used as an expansion target
// Meaning:
// Players must all choose "solo" mode
"rules": [ {
"name": "modeRule",
"type": "comparisonRule",
"measurements": [
"flatten(teams[*].players.playerAttributes[mode])"
],
"referenceValue": "solo",
"operation": "="
}]

// Meaning:
// Players of team red must all be "attacker" side
"rules": [ {
"name": "side_binding_red",
"type": "comparisonRule",
"measurements": [
"flatten(teams[red].players.playerAttributes[side])"
],
"referenceValue": "attacker",
"operation": "="
}]

// Meaning:
// Players of each team must pick the same side
"rules": [ {
"name": "side_binding",
"type": "comparisonRule",
"measurements": [
"teams[*].players.playerAttributes[side]"
],
"operation": "="
} ]

Type 3. Latency rule

Property nameDescriptionRequiredValue constraint
nameName of ruleYesA string of length 32. The value range is a~z, A~Z, 0~9, _ and is unique in the same ruleset
descriptionDescription of rulesNoString of length 256
typeType of ruleYesfixed "latencyRule"
maxLatencyMaximum latencyYesUnit is ms;
The value range is 0~999999;
can be used as an expansion target
partyAggregationProcessing mode of multiplayer requestNoValid options include each(each), minimum(min), maximum(max), average(avg) or any one(any) for a request's player. Default is each
// Meaning:
// The maximum delay from the player to the region does not exceed 150ms
"rules": [{
"name": "laytency_rule_example",
"type": "latencyRule",
"maxLatency": 150
}]

Type 4. Collection rule

Property nameDescriptionRequiredValue constraint
nameName of ruleYesA string of length 32. The value range is a~z, A~Z, 0~9, _ and is unique in the same ruleset
descriptionDescription of rulesNoString of length 256
typeType of ruleYesfixed "collectionRule"
measurementsExpressions of rulesYesAn expression whose calculation result is List, which can be List of number or List of string. Only 1 expression is supported temporarily for a collection rule.
referenceValueValue to compare with rule expressionsNoThe corresponding operation must be set to "contains" if referenceValue is defined. The collection rule treats the measurements result as a one-dimensional array (if the original result is two-dimensional, it will be automatically flattened into one dimension). The rule then counts elements matching the referenceValue within the flattened array.
If referenceValue is not defined, then the corresponding operation must be set to "intersection". The collection rule treats the measurements result as a two-dimensional array and attempts to compute the intersection across all sub-arrays.
For collection rules, the referenceValue parameter must be assigned a constant value (literal) and cannot be an expression.
operationOperation of collection ruleYesValid values: “intersection”, “contains”
Intersection: Logical AND operation across sub-arrays (e.g., [[1,2], [2,3]] → intersection [2]).
Contains: Boolean check for value existence, aggregated as a count metric.
minCountMinimum count of elements in result arrayYesSpecifies the minimum allowed number of elements in the collection rule's result set. Set this to 0 to disable the minimum constraint.
maxCountMaximum count of elements in result arrayYesSpecifies the maximum allowed number of elements in the collection rule's result set. Set this to 0 to disable the maximum constraint.
Tip

The calculation logic and effects of collecction rule is more abstract when comparing with other types of rules. We recommend using the Debug Rules feature in the ruleset editor to evaluate the behavior of set-based rules.

3.5 Rules Expansion

The matchmaking rules support the expansion of constraints of some fields to implement "time decay" matchmaking.

3.5.1 Structure of expansion item

Property nameDescriptionRequiredValue constraint
targetThe target property to relaxYescan be the following forms:

teams[red].minPlayers
teams[*].maxPlayers
rules[mySkillRule].maxDistance
rules[myLatencyRule].maxLatency
rules[myCollectionRule].referenceValue
stepsAll steps by passed timeYesThe count of steps should not exceed 10
steps/waitTimeSecondsThe absolute time passed since one player started matchmakingYesThe maximum waitTimeSeconds should not exceed the timeout limit defined in match config
steps/valueThe value of the targetYesThe minPlayers of a team cannot be less than one.
The maxDistance of distance rule cannot be less than minDistance.

e.g

// Meaning:
// By time elapsed 5s without result, modify the value of minDistance to 4
// By time elapsed 5s without results, modify the value of maxDistance to 15
// By time elapsed 10s without results, modify the value of maxDistance to 20
"rules": [
{
"name": "distanceRuleExample",
"type": "distanceRule",
"measurements": [
"avg(teams[*].players.playerAttributes[skill])"
],
"referenceValue": "avg(flatten(teams[*].players.playerAttributes[skill]))",
"minDistance":5,
"maxDistance":10,
}
]
"expansions": [
{
"target": "rules[distanceRuleExample].minDistance",
"steps": [{
"waitTimeSeconds": 5,
"value": 4
}]
},
{
"target": "rules[distanceRuleExample].maxDistance",
"steps": [{
"waitTimeSeconds": 5,
"value": 15
},{
"waitTimeSeconds": 10,
"value": 20
}
]
// Meaning:
// By time elapsed 5s without result, modify the value of referenceValue to 2
// By time elapsed 10s without results, modify the value of referenceValue to 3
"rules": [
{
"name": "natRuleExample",
"type": "collectionRule",
"description": "",
"measurements": [
"flatten(teams[*].players.playerAttributes[natType])"
],
"referenceValue": "1",
"operation": "contains",
"maxCount": 10,
"minCount": 1
}
]
"expansions": [
{
"target": "rules[natRuleExample].referenceValue",
"steps": [
{
"waitTimeSeconds": 5,
"value": "2"
},
{
"waitTimeSeconds": 10,
"value": "3"
}
]
}
]