GS2-Matchmaking
A feature that groups players based on conditions. It can be used to find opponents.
Script Triggers
By configuring createGatheringTriggerScriptId, completeMatchmakingTriggerScriptId, and changeRatingScript on the namespace, you can execute custom scripts when a gathering is created, when matchmaking completes, and when ratings change.
The main configurable event triggers and the script setting names are as follows.
createGatheringTriggerScriptId: When a gathering is createdcompleteMatchmakingTriggerScriptId: When matchmaking completeschangeRatingScript: When a rating changes
Push Notifications
The main push notifications you can configure and their setting names are as follows.
joinNotification: Notifies when joining a matchleaveNotification: Notifies when leaving a matchcompleteNotification: Notifies when a match is completedchangeRatingNotification: Notifies when a rating changes
All notifications can be delivered via GS2-Gateway and can also be forwarded as mobile push notifications to offline devices.
Master Data Management
By registering master data, you can configure the data and behaviors available in the microservice.
The types of master data are as follows.
RatingModel: Rating calculation settingsSeasonModel: Season period settings
In addition to registering master data from the management console, you can also reflect data from GitHub or build a workflow that registers it from CI using GS2-Deploy.
Additional Settings
Enabling enableDisconnectDetection detects disconnections during matchmaking, and disconnectDetectionTimeoutSeconds adjusts the timeout. By configuring enableCollaborateSeasonRating, when matchmaking completes, GS2-Matchmaking integrates with GS2-SeasonRating and generates a session that accepts results.
Transaction Actions
GS2-Matchmaking provides the following transaction actions.
- Verify action: Verify participation in a persistent gathering
By using “Verify participation in a persistent gathering” as a verify action, you can set up rewards that can only be received by players who belong to a specific season gathering (team or cluster). This allows safe control within a transaction of measures such as seasonal group competition events or incentives restricted to specific communities.
Use-Case Design Guide
Matchmaking with Party Tokens
If you want to refill the slot after one player leaves a completed match, or if you want to form a party in advance and then run matchmaking, use “party tokens”.
Each party member issues a player token (valid for 3 minutes) and sends it to the party leader. The leader sends them to the party-token issuing API to obtain a party token (valid for 10 minutes), and then invokes gathering creation or gathering search. This way, a gathering can be created or joined with all party members already present.
Matchmaking Between Parties
For example, in a 4 vs 4 battle where you do not want pre-formed parties to be split, you can first run a 4-player matchmaking to create per-party gatherings, and then have each party leader perform a 2-player matchmaking. This realizes party-vs-party matchmaking.
Communication Between Players During Matchmaking
If players need to interact before matchmaking completes, you can use one of the following two methods when creating the gathering.
- Creating a chat room with GS2-Chat: Suitable for low-frequency metadata exchange (up to 3 times per second)
- Launching a game server with GS2-Realtime: Suitable for high-frequency communication, or for letting players play before the required number of participants has been reached
Processing After Matchmaking Completes
When matchmaking completes, players can be notified using the following methods.
- In-game push notifications using GS2-Gateway
- Job registration via GS2-JobQueue
You can also execute arbitrary scripts using GS2-Script. When using GS2-Realtime, you can create a GS2-Realtime gathering within the GS2-Script executed after matchmaking completes, and notify the IP address and port information via push notifications or the job queue, guiding players to the game server.
Excluding Players via Blacklists
You can specify a blacklist of user IDs when creating or searching for a gathering. The blacklist specified at search time is added to the blacklist of the gathering joined. If you are on a blacklist, you are excluded from matchmaking.
Standard Matchmaking
Gathering
A gathering is a group of players grouped together by the matchmaker. The lifecycle of a gathering starts when a player explicitly creates it. Other players run matchmaking and, if the conditions match, they join the gathering.
When the number of players specified when the gathering was created is reached, the matchmaking result is notified to the players and the gathering is deleted from GS2-Matchmaking.
Attributes
A player can have up to 5 attribute values. When creating a gathering, you specify the range of each attribute value for the players you want to recruit.
For example, suppose you have a player with the following attribute values.
| Attribute Name | Attribute Value |
|---|---|
| GameMode | 1 |
| Stage | 10 |
| Level | 5 |
Let GameMode represent the game mode the player wants: whether to match against players worldwide (1) or per region (2=JP, 3=US, 4=EU). Let Stage represent the type of in-game stage the player wants to use for the match. Finally, Level is the player’s skill level.
Let’s consider what conditions the gathering should be created with when this player creates a gathering to find opponents.
| Attribute Name | Attribute Value (Min) | Attribute Value (Max) |
|---|---|---|
| GameMode | 1 | 1 |
| Stage | 10 | 10 |
| Level | 2 | 8 |
The range settings above would be appropriate. GameMode and Stage must match exactly, otherwise players cannot play under the conditions they want. For Level, target players within 3 above or below the player’s level.
By creating a gathering with these settings, players who meet the conditions will join the gathering when they submit a matchmaking request.
Roles
When creating a gathering, you need to set the number of players to recruit. When setting this, you can set the number of players to recruit per role. If there are no specific roles in the game, configure a single role named “default” and specify the number of players you want to gather as the capacity of that role.
You need to specify roles in games that have role-based gameplay. For example, in an MMORPG, players have in-game roles such as tank, healer, and DPS depending on their class. If matchmaking ends up with 4 healers, the content cannot be cleared. Each role must be matched in a balanced way.
In such cases, configure roles and recruitment numbers as follows.
| Role Name | Capacity |
|---|---|
| Tank | 1 |
| Healer | 1 |
| DPS | 2 |
Then, when a player submits a matchmaking request, they specify not only attribute values but also their own role. This way, slot management is performed per role and matchmaking is executed.
Role Aliases
Here is an example of role-based matchmaking with more complex condition settings. Suppose DPS has two sub-types: melee DPS and ranged DPS. The basic matchmaking conditions can be as explained above, but the gathering creator might prefer to have exactly one melee DPS and one ranged DPS.
In that case, the condition settings would be as follows.
| Role Name | Capacity |
|---|---|
| Tank | 1 |
| Healer | 1 |
| Melee DPS | 1 |
| Ranged DPS | 1 |
Players then specify “melee DPS” or “ranged DPS” rather than “DPS” when matchmaking. However, with this setup, a player who says “I’m fine being either melee or ranged as long as 2 DPS are gathered” can no longer match.
That is where the role alias feature is useful. The part where players set their role as “melee DPS” or “ranged DPS” does not change, but the condition settings used when creating the gathering change.
| Role Name | Aliases | Capacity |
|---|---|---|
| Tank | [] | 1 |
| Healer | [] | 1 |
| DPS | [Melee DPS, Ranged DPS] | 2 |
This way, players whose role is “melee DPS” or “ranged DPS” can also fill DPS slots.
Rating Calculation
A feature is provided to calculate a rating value that represents the player’s strength. The initial rating value is 1500 and goes up or down depending on the result of the game.
When players with a large rating gap play against each other and the higher-rated player loses, the higher-rated losing player’s rating drops significantly, while the lower-rated winning player’s rating rises significantly. This characteristic produces ratings that reflect player skill.
Voting
To change a rating value, a voting process is required. When the match is over and the ranking is finalized, each player sends the game result to the server. The server accepts votes per gathering and closes voting when all players have voted, or 5 minutes after the first player voted.
The server takes a majority vote and finalizes the result. This process means that even if there are malicious players who vote dishonestly, their votes do not influence the final result. A new rating value is calculated and reflected based on the ranking finalized by the majority vote.
Tied Voting Results
If the majority vote is tied and the final result cannot be determined, the rating calculation is not performed. Therefore, it is difficult to obtain correct rating values in a 1v1 game. To solve this, you can devise a workaround such as matchmaking a third player who is not directly involved in the match and having that player vote from a third-party perspective.
Expiration of a Gathering
In principle, once a gathering is created, it will not be deleted until matchmaking succeeds or all players have left. Especially in games with few players, if a player ends the game during matchmaking without calling the cancel API, it is possible that another player submits a matchmaking request, matchmaking succeeds, and yet the player who created the gathering no longer exists.
To easily solve this problem, you can set an expiration for the gathering at creation time. By specifying a specific time, or an elapsed time from creation, the gathering will be deleted at that time. At that point, any players who have already joined the gathering will be automatically removed.
Implementation Examples
Creating a Gathering
var result = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).CreateGatheringAsync(
player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer {
Attributes = new [] {
new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
Name = "stage",
Value = 1,
},
new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
Name = "level",
Value = 10,
},
},
},
attributeRanges: new [] {
new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange {
Name = "stage",
Min = 1,
Max = 1,
},
new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange {
Name = "level",
Min = 0,
Max = 10,
},
},
capacityOfRoles: new [] {
new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole {
RoleName = "default",
Capacity = 4,
},
},
);
var item = await result.ModelAsync(); const auto Future = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->CreateGathering(
MakeShared<Gs2::Matchmaking::Model::FPlayer>()
->WithAttributes([]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FAttribute>>>();
v->Add(MakeShared<Gs2::Matchmaking::Model::FAttribute>()
->WithName(TOptional<FString>("stage"))
->WithValue(TOptional<int32>(1)));
v->Add(MakeShared<Gs2::Matchmaking::Model::FAttribute>()
->WithName(TOptional<FString>("level"))
->WithValue(TOptional<int32>(10)));
return v;
}()),
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FAttributeRange>>>();
v->Add(MakeShared<Gs2::Matchmaking::Model::FAttributeRange>()
->WithName(TOptional<FString>("stage"))
->WithMin(TOptional<int32>(1))
->WithMax(TOptional<int32>(1)));
v->Add(MakeShared<Gs2::Matchmaking::Model::FAttributeRange>()
->WithName(TOptional<FString>("level"))
->WithMin(TOptional<int32>(0))
->WithMax(TOptional<int32>(10)));
return v;
}(), // attributeRanges
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FCapacityOfRole>>>();
v->Add(MakeShared<Gs2::Matchmaking::Model::FCapacityOfRole>()
->WithRoleName(TOptional<FString>("default"))
->WithCapacity(TOptional<int32>(4)));
return v;
}(), // capacityOfRoles
nullptr, // allowUserIds
nullptr, // expiresAt
nullptr // expiresAtTimeSpan
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Running Matchmaking
var items = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).DoMatchmakingAsync(
player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer {
Attributes = new [] {
new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
Name = "stage",
Value = 1,
},
new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
Name = "level",
Value = 10,
},
},
},
).ToListAsync(); const auto It = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->DoMatchmaking( // player
);
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Leaving a Gathering
var result = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Gathering(
gatheringName: "gathering-0001"
).CancelMatchmakingAsync(
); const auto Future = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Gathering(
"gathering-0001" // gatheringName
)->CancelMatchmaking(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Getting the Rating Value
var item = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Rating(
ratingName: "rating-0001"
).ModelAsync(); const auto Domain = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Rating(
"rating-0001" // ratingName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Retrieving a Ballot
var item = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Ballot(
ratingName: "rating-0001",
gatheringName: "gathering-0001",
numberOfPlayer: 4,
keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
).ModelAsync();
var body = result.Body;
var signature = result.Signature; const auto Domain = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Ballot(
"rating-0001", // ratingName
"gathering-0001", // gatheringName
4, // numberOfPlayer
"key-0001" // keyId
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Submitting a Vote
var result = await gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001"
).VoteAsync(
ballotBody: "ballotBody",
ballotSignature: "ballotSignature",
gameResults: new [] {
new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
{
Rank = 1,
UserId = "user-0001",
},
new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
{
Rank = 2,
UserId = "user-0002",
},
new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
{
Rank = 2,
UserId = "user-0003",
},
new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
{
Rank = 3,
UserId = "user-0004",
},
}
); const auto Future = Gs2->Matchmaking->Namespace(
"namespace-0001" // namespaceName
)->Vote(
"ballotBody",
"ballotSignature",
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FGameResult>>>();
v->Add({'rank': 1, 'userId': 'user-0001'});
v->Add({'rank': 2, 'userId': 'user-0002'});
v->Add({'rank': 2, 'userId': 'user-0003'});
v->Add({'rank': 3, 'userId': 'user-0004'});
return v;
}() // gameResults
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Frequently Asked Questions
I want to list existing gatherings and let players choose one
GS2-Matchmaking does not provide such a feature. This is not for technical reasons; it is a policy decision by us as fellow game developers, intended to avoid harming the player experience.
If players choose from a list of existing gatherings, you can present them with the best way to join. However, between retrieving the list and actually performing the join operation, a delay is introduced by the user’s input. Because of this delay, it is unavoidable that, by the time players select the gathering they want to join, the gathering is already full.
GS2 developers have played many games like this in the past and felt enormous frustration with them. We are confident that GS2-Matchmaking provides a mechanism that can automatically find the most appropriate gathering for the player without requiring them to select one themselves. For that reason, we do not provide such a feature.
I want to set a password on a gathering
Set the password as one of the attribute values and use it as part of the matchmaking conditions.
Season Matchmaking
Season matchmaking creates a gathering that persists for a specified period. Rather than matchmaking for real-time matches, the common use case is to form clusters that persist for a specific period, and combine them with GS2-Ranking2 cluster rankings to realize rankings within a gathering.
Season
When using season matchmaking, you set the period by specifying an event in GS2-Schedule. If the event has a repeat setting, a new gathering is formed each time it repeats.
Tier
By combining season matchmaking with GS2-Experience, players within a specific rank can be matched together. The GS2-Ranking2 cluster ranking has a feature for setting ranking rewards, allowing top-ranked players to be granted experience so they can move up to a higher tier.
Maximum Number of Players
In season matchmaking, up to 1000 players can be matched.
Matchmaking Conditions
Season matchmaking does not allow complex search conditions like standard matchmaking. There is only one parameter available for search conditions: the rank from GS2-Experience. The value is not passed as an API argument; GS2-Matchmaking obtains the value internally from GS2-Experience.
If the integration with GS2-Experience is not used, matchmaking targets all players.
Leaving a Persistent Gathering
With standard matchmaking, you could leave a matched gathering. However, with season matchmaking you cannot leave; if you have already matched and submit another matchmaking request, the gathering you have already joined is returned.
In other words, during the season there is in principle no way to re-match into a different gathering. There is one exception: when the persistent gathering is deleted. By deleting the persistent gathering, the players in it will be matched into a new gathering the next time they perform matchmaking.
Matchmaking Process Priority
In season matchmaking, when matchmaking, the system searches for a persistent gathering of the same tier that has not yet reached its capacity and joins it. If no joinable gathering exists, the API automatically creates a gathering, performs the join process, and then responds.
If multiple joinable gatherings exist, the gathering with more participants is given priority.
Behavior When Matchmaking Conflicts
A matchmaking join operation generally completes within a few milliseconds, but even so, the same gathering can be selected as a join candidate simultaneously. To prevent more players than the configured capacity from joining, GS2-Matchmaking takes a lock on the gathering before processing the player’s join. As a result, even if a joinable gathering exists, it is excluded from matchmaking candidates for a brief moment while another player’s join is being processed, and another candidate is selected instead.
Because of this behavior, if you prepare a persistent gathering with a maximum capacity of 1000 and 1000 players of the same tier submit matchmaking requests at exactly the same time, you will not necessarily end up with a single persistent gathering of 1000 participants; the players may be split across multiple persistent gatherings. At this time, players are joined into the gathering with the most participants whenever possible, so the following kinds of results are generally expected.
| Participants in Gathering A | Participants in Gathering B | Participants in Gathering C |
|---|---|---|
| 1000 | - | - |
| 999 | 1 | - |
| 998 | 1 | 1 |
| 997 | 2 | 1 |
| 995 | 4 | 1 |
| 995 | 5 | - |
| 990 | 8 | 2 |
This result is just one example; different patterns can occur.