Skip to main content

Multiplayer V2

Last updated on

Overview

AccelByte Foundations’ Multiplayer V2 is a session-based player matchmaker that offers more flexibility than our previous matchmaking service. Game developers can now use their own matchmaking functions to override the defaults imposed by our original service.

How It Works

Multiplayer V2 works in almost the exact same way as the original matchmaking service. The difference is that it no longer uses the Lobby service to manage parties. Instead, parties are managed by the Session service. The Lobby service is still used to convey notifications back to the game client. The flow is as follows:

Multiplayer V2

Party:

The Client asks the Session service to create a party. A Party Invitation notification is then relayed from the Session service through the Lobby.

Matchmaking:

The Client asks the Matchmaking service to request a match. The Matchmaking service then asks the Session service to create a session. Whether the session creation succeeds or fails, a notification will be conveyed back to the Client through the Lobby.

Manage Multiplayer V2 in the Admin Portal

Create a Session Template

  1. In the Admin Portal, select your intended game title.

  2. In the left-hand menu, navigate to Game Management, expand New Matchmaking, and select Session Templates.

  3. Select + Add Session Template in the top right-hand corner.

    Multiplayer V2

    The Add Session Template form will appear.

  4. Fill in the following fields:

    Multiplayer V2

    • Session Template Name: enter a name for your session template.
    • Session Type: select the session type from the dropdown menu.
    • Min Players: set the minimum number of players allowed in the session.
    • Max Players: set the maximum number of players allowed in the session.
    • Invite Timeout: set the time limit (in seconds) before an invite from the session times out.
    • Inactive Timeout: set how long (in seconds) the session will wait before timing out a player that has not responded.
    • Joinability: select the joinability from the dropdown menu.
    • Requested Regions: select the regions this session can be used in from the dropdown menu.
  5. Once the form is completed, click Add to save your session.

Create a Match Ruleset

  1. In the Admin Portal, select your intended game title.

  2. In the left-hand menu, navigate to Game Management, click New Matchmaking, and select Match Configuration.

  3. Select + Create Rulesets in the top right-hand corner.

    Multiplayer V2

    The Create Match Ruleset page will appear.

  4. Fill in the following fields:

    Multiplayer V2

    • Ruleset Name: enter a name for your match ruleset.
    • Configuration (JSON): input your configuration in JSON format. A configuration template will appear in this field which you can edit for your ruleset or replace with your own JSON.
  5. Once the fields are filled out, click Create to save your ruleset.

Create a Match Pool

  1. In the Admin Portal, select your intended game title.

  2. In the left-hand menu, navigate to Game Management, click New Matchmaking, and select Match Configuration.

  3. Select the Match Pools tab at the top of the page and then click + Add Match Pools in the top right-hand corner.

    Multiplayer V2

    The Add Match Pools form will appear.

  4. Fill in the following fields:

    Multiplayer V2

    • Match Pool Name: enter a name for your match pool name.
    • Match Ruleset: select a Match Ruleset from the dropdown menu.
    • Session Template: select a Session Template from the dropdown menu.
    • Match Function: select a Match Function from the dropdown menu.
  5. Once you have completed the fields, click Add to save your match pool.

Implement Multiplayer V2 Using the Client SDKs

Party

Create a Party

If the optional request is not filled, it will get the value from the configuration used.

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

FAccelByteModelsV2PartyCreateRequest Request;
Request.ConfigurationName = ConfigurationName; // MANDATORY
Request.Joinability = EAccelByteV2SessionJoinability::INVITE_ONLY; // Optional
Request.Members = {{OtherPartyMemberId}}; // Optional
Request.Attributes.JsonObject = MakeShared<FJsonObject>(); // Optional
Request.Attributes.JsonObject->SetStringField("PartyAttribute", "Attribute1"); // Optional
Request.MaxPlayers = 10; // Optional
Request.MinPlayers = 1; // Optional
Request.InactiveTimeout = 30; // Optional
Request.InviteTimeout = 86400; // Optional

ApiClient->Session.CreateParty(Request, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when create party success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when create party failed / error
}));

Retrieve parties

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.GetMyParties(THandler<FAccelByteModelsV2PaginatedPartyQueryResult>::CreateLambda(
[&](const FAccelByteModelsV2PaginatedPartyQueryResult& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Retrieve Party Details by Party ID

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.GetPartyDetails(PartyId, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Update Party Data

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

FAccelByteModelsV2PartyUpdateRequest Request;
Request.Version = PartyDataVersion; // Mandatory, must be the same version as latest party data
Request.Joinability = EAccelByteV2SessionJoinability::INVITE_ONLY; // Optional
Request.Attributes.JsonObject = MakeShared<FJsonObject>(); // Optional
Request.Attributes.JsonObject->SetStringField("PartyAttribute", "Attribute1"); // Optional
Request.MaxPlayers = 10; // Optional
Request.MinPlayers = 1; // Optional
Request.InactiveTimeout = 30; // Optional
Request.InviteTimeout = 86400; // Optional

ApiClient->Session.UpdateParty(PartyId, Request, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Invite Player to Party

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.SendPartyInvite(PartyId, UserIdToInvite, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Join a party

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.JoinParty(PartyId, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Reject a party invite

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.RejectPartyInvite(PartyId, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Leave a current party

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.LeaveParty(PartyId, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Kick a player from party

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.KickUserFromParty(PartyId, UserIdToKick, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Promote member to party leader

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.PromotePartyLeader(PartyId, NewPartyLeaderId, THandler<FAccelByteModelsV2PartySession>::CreateLambda(
[&](const FAccelByteModelsV2PartySession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when get my party failed / error
}));

Listen to party notifications

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Lobby.SetV2PartyInvitedNotifDelegate(Api::Lobby::FV2PartyInvitedNotif::CreateLambda(
[&](const FAccelByteModelsV2PartyInvitedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2PartyMembersChangedNotifDelegate(Api::Lobby::FV2PartyMembersChangedNotif::CreateLambda(
[&](const FAccelByteModelsV2PartyMembersChangedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2PartyJoinedNotifDelegate(Api::Lobby::FV2PartyJoinedNotif::CreateLambda(
[&](const FAccelByteModelsV2PartyUserJoinedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2PartyRejectedNotifDelegate(Api::Lobby::FV2PartyRejectedNotif::CreateLambda(
[&](const FAccelByteModelsV2PartyUserRejectedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2PartyKickedNotifDelegate(Api::Lobby::FV2PartyKickedNotif::CreateLambda(
[&](const FAccelByteModelsV2PartyUserKickedEvent& Notif)
{
// Do something when receive notification
}));

Game sessions

Create a game session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

FAccelByteModelsV2GameSessionCreateRequest Request;
Request.ConfigurationName = ConfigurationName; // MANDATORY
Request.Joinability = EAccelByteV2SessionJoinability::INVITE_ONLY; // Optional
Request.Type = EAccelByteV2SessionConfigurationServerType::DS; // Optional
Request.ClientVersion = GameServerVersion; // Optional
Request.ServerName = LocalServerName; // Optional
Request.Deployment = Deployment; // Optional
Request.RequestedRegions = {"us-west-1", "us-west2"}; // Optional

// Optional
TArray<FString> TeamA = {TeamAUserId1, TeamAUserId2};
TArray<FString> TeamB = {TeamBUserId1, TeamBUserId2};
TArray<FAccelByteModelsV2GameSessionTeam> Teams;
Teams.Add({TeamA});
Request.Teams = Teams;

// Optional
Request.Attributes.JsonObject = MakeShared<FJsonObject>();
Request.Attributes.JsonObject->SetStringField("PartyAttribute", "Attribute1");

Request.MaxPlayers = 10; // Optional
Request.MinPlayers = 1; // Optional
Request.InactiveTimeout = 30; // Optional
Request.InviteTimeout = 86400; // Optional


ApiClient->Session.CreateGameSession(Request,
THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// Do something when operation success
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when operation error / failed
}));

Retrieve game sessions

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.GetMyGameSessions(
THandler<FAccelByteModelsV2PaginatedGameSessionQueryResult>::CreateLambda(
[&](const FAccelByteModelsV2PaginatedGameSessionQueryResult& Result)
{
// Do something when operation success
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when operation error / failed
}));

Retrieve game session details

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.GetGameSessionDetails( SessionId,
THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// Do something when operation success
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when operation error / failed
}));

Update game session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

FAccelByteModelsV2GameSessionUpdateRequest Request;
Request.Version = PartyDataVersion; // Mandatory, must be the same version as current data in the backend
Request.Joinability = EAccelByteV2SessionJoinability::INVITE_ONLY; // Optional
Request.Attributes.JsonObject = MakeShared<FJsonObject>(); // Optional
Request.Attributes.JsonObject->SetStringField("AttributeName", "Attribute1"); // Optional
Request.MaxPlayers = 10; // Optional
Request.MinPlayers = 1; // Optional
Request.InactiveTimeout = 30; // Optional
Request.InviteTimeout = 86400; // Optional

ApiClient->Session.UpdateGameSession(GameSessionID, Request, THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Delete a game session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();
ApiClient->Session.DeleteGameSession(SessionId, FVoidHandler::CreateLambda([]
{
// successfully deleted game session
}),
FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& Message)
{
// error deleting game session
}));

Invite player to session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.SendGameSessionInvite(SessionId, UserIdToInvite, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Join a game session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.JoinGameSession(SessionId, THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Reject game session invite

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.RejectGameSessionInvite(GameSessionID, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Leave a game session

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Session.LeaveGameSession(SessionId, FVoidHandler::CreateLambda(
[&]
{
// do something when success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// do something when failed / error
}));

Listen to game session notification

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->Lobby.SetV2GameSessionInvitedNotifDelegate(Api::Lobby::FV2GameSessionInvitedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserInvitedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2GameSessionMembersChangedNotifDelegate(Api::Lobby::FV2GameSessionMembersChangedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionMembersChangedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2GameSessionJoinedNotifDelegate(Api::Lobby::FV2GameSessionJoinedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserJoinedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2GameSessionRejectedNotifDelegate(Api::Lobby::FV2GameSessionRejectedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserRejectedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2GameSessionKickedNotifDelegate(Api::Lobby::FV2GameSessionKickedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserKickedEvent& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2GameSessionUpdatedNotifDelegate(Api::Lobby::FV2GameSessionUpdatedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Notif)
{
// Do something when receive notification
}));

ApiClient->Lobby.SetV2DSStatusChangedNotifDelegate(Api::Lobby::FV2DSStatusChangedNotif::CreateLambda(
[&](const FAccelByteModelsV2DSStatusChangedNotif& Notif)
{
// Do something when receive notification
}));

Matchmaking

Create a match ticket

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

FAccelByteModelsV2MatchTicketOptionalParams Optionals;

Optionals.Attributes.JsonObject = MakeShared<FJsonObject>();
Optionals.Attributes.JsonObject->SetStringField("TestAttribute1", "AttributeValue");

Optionals.Latencies = ApiClient->Qos.GetCachedLatencies();
Optionals.SessionId = PartySessionId;

ApiClient->MatchmakingV2.CreateMatchTicket(MatchPoolName,
THandler<FAccelByteModelsV2MatchmakingCreateTicketResponse>::CreateLambda(
[&](const FAccelByteModelsV2MatchmakingCreateTicketResponse& Result)
{
// Do something when operation success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCodes, const FString& ErrorMessage)
{
// Do something when operation failed
}),
Optionals);

Get ticket details

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->MatchmakingV2.GetMatchTicketDetails(TicketID,
THandler<FAccelByteModelsV2MatchmakingGetTicketDetailsResponse>::CreateLambda(
[&](const FAccelByteModelsV2MatchmakingGetTicketDetailsResponse& Result)
{
// Do something when operation success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCodes, const FString& ErrorMessage)
{
// Do something when operation failed
}));

Delete match ticket

FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();

ApiClient->MatchmakingV2.DeleteMatchTicket(TicketID,
FVoidHandler::CreateLambda([&]
{
// Do something when operation success
}),
FErrorHandler::CreateLambda([&](int32 ErrorCodes, const FString& ErrorMessage)
{
// Do something when operation failed
}));