GS2-Matchmaking

Matchmaking function

This function groups players based on criteria. This can be used to find opponents.

Standard Matchmaking

Gathering

A Gathering is a group of players who have been grouped together by Matchmakers. The lifecycle of a gathering is explicitly created by the first player. Other players perform matchmaking and join the gathering if they meet the criteria.

When the number of players specified when the gathering was created is reached, players are notified of the matchmaking result and the gathering is removed from the waiting list.

Attributes

Players can have up to 5 attributes. When creating a Gathering, specify a range of attribute values for each player you want to recruit to create the Gathering.

For example, suppose you have a player with the following attribute values

Attribute NameAttribute Value
GameMode1
Stage10
Level5

GameMode is the game mode you want to use, whether it is matchmaking for all players in the game (1) or for each region (2=JP, 3=US, 4=EU). Let Stage be the type of stage in the game you want to use for matchmaking. Finally, Level is the skill level of the player.

If this player wants to create a gathering to find opponents, let’s consider what kind of conditions would be appropriate to set for the gathering.

Attribute NameAttribute Value (Min)Attribute Value (Max)
GameMode11
Stage1010
Level28

A range setting like the one shown above is recommended. GameMode and Stage must match exactly or the player will not be able to play under the conditions he/she desires. The level should be 3 players before or after the player’s level.

By creating a gathering, players who meet the criteria will join the gathering when they submit a matchmaking request.

Roles

When you create a gathering, you must set the number of players. When setting the number of recruits, you can set the number of recruits for each role. If there is no specific role in the game, you can set a role called Default and specify the number of people you want to gather in that role’s recruiting pool.

The case where you need to specify a role is in a game where there are roles in the game. For example, in MMORPGs, there are in-game roles such as tank, healer, and dps, depending on the player’s profession. And as a result of matchmaking, four healers cannot conquer the content of the game. Each role must be balanced in the matchmaking process.

In this specification, the following roles and the number of recruiters are set.

Role NameNumber of Recruits
Tank1
Healer1
DPS2

Then, when a player submits a matchmaking request, he/she sets not only the attribute values, but also his/her role. In this way, each role will manage the quota of applicants and matchmaking will be performed.

Role Aliases

Here is an example of role-based matchmaking based on more complex condition settings. Suppose there are two types of DPS: melee DPS and ranged DPS. The basic matchmaking conditions can be as explained above, but the Gathering Creator is particular about them. Let’s say you want a melee DPS and a ranged DPS to come in.

In this case, the condition settings would be as follows.

Role NameNumber of Recruits
Tank1
Healer1
Melee DPS1
Ranged DPS1

The player will then specify “melee DPS” or “ranged DPS” instead of “DPS” when matchmaking. However, as it stands, matchmaking will no longer be possible with those who say, “I don’t care if it’s melee or ranged as two DPSs get together.”

Role NameAliasNumber of Recruits
Tank[]1
Healer[]1
DPS[Melee DPS, Ranged DPS]2

This will allow players who are “melee DPS” or “ranged DPS” in their roles to be included in the DPS recruitment slots.

Rating Calculation

There is a function to calculate the rating value, which expresses the strength of a player. The initial rating value is 1500, and the value goes up or down depending on the results of the game.

When players with large differences in rating play against each other and the player with the higher rating loses, the rating of the losing player with the higher rating will decrease significantly, while the rating of the winning player with the lower rating will increase significantly. This feature allows the calculation of ratings that reflect the players’ abilities.

Voting

To change the rating values, a voting process is required. Once the game is over and the standings are determined, each player sends the results of the game to the server. The server will accept votes for each match and close the poll after all players have voted or 5 minutes have passed since the first player voted.

The server uses a majority of the ballots to determine the final result. This process ensures that any malicious players who vote incorrectly will not be able to influence the results. Based on the ranking determined by the majority vote, a new rate value is calculated and reflected.

Fragmentation of Voting Results

If the server attempts a majority vote and the result is a tie and the final result cannot be determined, no rate calculation will be performed. This makes it difficult to determine the correct rate value in a 1v1 game. To solve this problem, it is necessary to devise a solution such as matchmaking a third player who is not directly involved in the game behind the scenes and having that player vote from a third party perspective.

Expiration of Gathering

Once a gathering is created, it will not be deleted until matchmaking is complete or all players have left the gathering. If a player leaves the game without calling the Cancel API during matchmaking, especially in a game with few players If a player leaves the game during matchmaking without calling the Cancel API, another player may submit a matchmaking request, and even if the matchmaking is successful, there may be no players who have already created a gathering.

A simple solution to this problem is to set the validity period of the gathering at the time of creation. By specifying a specific time, or an elapsed time from the time of creation, the gathering will be deleted at that time. Players who have already joined the gathering will be automatically removed from the gathering.

Example of implementation

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;

Perform matchmaking process

    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());
    }

Exit from 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;

Get rate 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 item = Domain.Model();

Retrieve 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 item = Domain.Model();

Run Poll

    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 would like to list the existing gatherings and let the player select one.

GS2-Matchmaking does not offer this feature. This is not for technical reasons, but because it is our policy as a game developer not to spoil the player’s experience.

If it is in the form of a choice among the gatherings that exist, it can present the best way for the player to participate in the gathering. However, there is a delay caused by user operations from the time the Gathering list is retrieved to the time the user actually performs the join operation. This delay makes it inevitable that the Gathering is already full when the user selects the Gathering he/she wants to join.

The GS2 developers have played many such games in the past and found them extremely frustrating. GS2-Matchmaking prides itself on providing a mechanism that automatically finds the best Gathering without requiring players to select their own Gathering. For this reason, we do not offer this feature.

I want to set a password for the Gathering.

Set the password as an attribute value and make it part of the matchmaking criteria.

Season Matchmaking

Season Matchmaking creates a Gathering that is persistent for a specific period of time. Rather than matchmaking for real-time competition, the general usage is to form clusters that persist for a specific period of time and combine GS2-Ranking2 cluster ranking to achieve ranking within a gathering.

Season

In using Season Matchmaking, a period of time is set by specifying an event in GS2-Schedule. If the event has a repeat setting, a new Gathering will be formed each time it is repeated.

Tear

Combining GS2-Experience with Season Matchmaking allows matchmaking between specific ranks. GS2-Ranking2 cluster ranking has the ability to set ranking rewards, allowing players to move up to higher tiers by awarding experience to the top ranked players.

Maximum number of players

The maximum number of players in a season matchmaking session is 1000.

Matchmaking Requirements

Season Matchmaking does not allow for complex search criteria like Standard Matchmaking. There is only one parameter available for search conditions, the rank of GS2-Experience, and the value is not taken as an API argument, but GS2-Matchmaking gets the value from GS2-Experience in an internal process.

If the linkage function with GS2-Experience is not used, matchmaking is performed for all players.

Exit from Persistent Gathering

In Standard Matchmaking, a player could leave a Gathering that had been Matchmaking. However, in Season Matchmaking, it is not possible to exit, and if you submit a Matchmaking Request while you have already made a Matchmaking, the Gathering you have already joined will respond.

In other words, there is in principle no way to re-matchmaking to another Gathering during the season. There is one exception, which is when a player deletes a permanent Gathering. By deleting a permanent Gathering, a player in that Gathering will be matched to a new Gathering when he/she re-matches.

Priority of Matchmaking Process

In the season matchmaking process, a permanent gatherer with the same tier will be selected to join a gatherer that has not yet reached the maximum number of participants at the time of matchmaking. If there is no available Gathering, the API will automatically create a Gathering, process the participation, and then respond to the API.

If there are multiple available Gatherings, the Gathering with more participants will be given priority.

Behavior when matchmaking conflicts

Matchmaking participation is generally processed in a few milliseconds or less, but it is still possible for the same gathering to be selected as a candidate for participation at the same time. In such cases, GS2-Matchmaking will lock the Gathering before processing the players’ participation in order to prevent more players than the specified number from participating in the Gathering. Therefore, even if there is an available Gathering, it will not be considered for matchmaking for a short period of time while other players are in the process of joining the Gathering, and other candidates will be selected.

This means that if you have a permanent gathering that can accommodate up to 1000 players, and you submit a matchmaking request for 1000 players from the same tier at the same time, you will not necessarily get a single permanent gathering with 1000 participants, but you may end up with multiple permanent gatherings, each of which will have its own matchmaking process. It is possible that the Gathering will be divided into several permanent Gatherings. In this case, priority will be given to the Gathering with the highest number of participants, so the following results are generally expected.

Number of participants in Gathering ANumber of participants in Gathering BNumber of participants in Gathering C
1000--
9991-
99811
99721
99541
9955-
99082

This result is just an example; different patterns can occur.

Detailed Reference