ゲームセッションを統合する
注釈:本資料はAI技術を用いて翻訳されています。
概要
AccelByte Gaming Services (AGS) Session を通じて、AGS Game SDK または Extend SDK を使用してゲームセッションを管理できます。この記事では、ゲームセッションの統合手順を説明し、以下のコードスニペットを提供します。
- ゲームセッションの作成
- ゲームセッションリストのクエリ
- 現在のユーザーのゲームセッションの取得
- ゲームセッションの詳細の取得
- ゲームセッションの更新
- ゲームセッションの削除
- プレイヤーのゲームセッションへの招待
- ゲームセッションへの参加
- ゲームセッション招待のキャンセル
- ゲームセッション招待の拒否
- ゲームセッションからの退出
- ゲームセッションリーダーの昇格
- プレイヤーのゲームセッションからのキック
- ゲームセッション通知のリスニング
- ゲームセッションの更新
- 切断後の再接続時のプレイヤーセッションの処理
ゲームセッションの作成
作成リクエストに基づいて新しいゲームセッションを作成します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->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 is successful
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
SessionV2GameSessionCreateRequest createGameSessionRequest = new SessionV2GameSessionCreateRequest
{
joinability = SessionV2Joinability.OPEN,
configurationName = configurationTemplateName
};
session.CreateGameSession(createGameSessionRequest, result =>
{
if (result.IsError)
{
// Do something if CreateGameSession has an error
Debug.Log($"Error CreateGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if CreateGameSession succeeds
});
ApimodelsCreateGameSessionRequest newSessionRequest = new ApimodelsCreateGameSessionRequest()
{
ConfigurationName = "<configuration template name>",
Joinability = "OPEN"
};
var response = sdk.Session.GameSession.CreateGameSessionOp
.Execute(newSessionRequest, sdk.Namespace);
if (response != null)
{
//do something if session is created
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
configurationName := "configurationtemplatename"
joinability := "OPEN"
body := sessionclientmodels.ApimodelsCreateGameSessionRequest {
ConfigurationName: &configurationName,
Joinability: &joinability,
}
namespace := "mygame"
input := &game_session.CreateGameSessionParams{
Body: &body,
Namespace: namespace,
}
result, err := gameSessionService.CreateGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
ApimodelsGameSessionResponse response;
try {
ApimodelsCreateGameSessionRequest reqBody = ApimodelsCreateGameSessionRequest.builder()
.configurationName("<configuration template name>")
.joinability("OPEN")
.build();
response = gameSessionWrapper.createGameSession(CreateGameSession.builder()
.namespace("<namespace>")
.body(reqBody)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if session is created
}
import accelbyte_py_sdk.api.session as session_service
import accelbyte_py_sdk.api.session.models as session_models
result, error = session_service.create_game_session(
body=session_models.ApimodelsCreateGameSessionRequest()
.with_configuration_name("config-template-name")
.with_joinability("OPEN"),
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションリストのクエリ
リクエストと同じ属性を持つすべてのゲームセッションをクエリします。 サポートされている操作:
EAccelByteV2SessionQueryComparisonOp::EQUAL(equalsと同様の特定の値とのクエリ比較)EAccelByteV2SessionQueryComparisonOp::NOT_EQUAL(特定の値とのnotEqualsクエリ)EAccelByteV2SessionQueryComparisonOp::CONTAINS(inと同様のクエリ)EAccelByteV2SessionQueryComparisonOp::NOT_CONTAINS(notInと同様のクエリ)EAccelByteV2SessionQueryComparisonOp::GREATER_THAN(greaterThanと同様のクエリ)EAccelByteV2SessionQueryComparisonOp::GREATER_THAN_EQUAL(greaterThanEqualsと同様のクエリ)EAccelByteV2SessionQueryComparisonOp::LESS_THAN(lessThanと同様のクエリ)EAccelByteV2SessionQueryComparisonOp::LESS_THAN_EQUAL(lessThanEqualsと同様のクエリ)
⚠️ 警告: パフォーマンスに関する考慮事項
クエリパフォーマンスへの影響:
- 複数の属性を同時にクエリすると、クエリパフォーマンスと応答時間に大きな影響を与えます
- 追加のクエリ条件ごとに計算の複雑さとデータベース負荷が増加します
- 複数の
NOT_EQUAL、NOT_CONTAINS、または範囲操作を含む複雑なクエリは特にリソース集約的です
ベストプラクティス:
NOT_EQUAL操作を避ける - これらはフルテーブルスキャンを必要とし、大規模なデータセットには最適化されていませんEQUAL操作を優先する - これらは最適なパフォーマンスのために等値、ソート、範囲(ESR)インデックス原則を活用します- クエリの複雑さを制限する - フィルタリングのニーズに必要な最小限の属性数を使用します
複雑なクエリ要件やパフォーマンス最適化戦略については、AccelByteサポートに相談して、特定のゲームフローとスケール要件に合わせたベストプラクティスの推奨事項を入手してください。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
// query with specific value `equals`
FAccelByteModelsV2GameSessionQuery Query;
Query.AddParam("map", EAccelByteV2SessionQueryComparisonOp::EQUAL, "SampleMap");
// query with `in`
TArray<FString> PossibleValues;
PossibleValues.Add(TEXT("solo"));
PossibleValues.Add(TEXT("adventure"));
Query.AddParam("custom_game_mode", EAccelByteV2SessionQueryComparisonOp::CONTAINS, PossibleValues);
// query with range value `greaterThanEquals`
constexpr int32 QueriedGreater = 2;
FAccelByteModelsV2GameSessionQuery Query;
Query.AddParam("level", EAccelByteV2SessionQueryComparisonOp::LESS_THAN_EQUAL, QueriedGreater);
int64 Offset = 0;
int64 Limit = 20;
ApiClient->Session.QueryGameSessions(
Query,
THandler<FAccelByteModelsV2PaginatedGameSessionQueryResult>::CreateLambda(
[&](const FAccelByteModelsV2PaginatedGameSessionQueryResult& Result)
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when the operation fails or has an error
}),
Offset,
Limit);
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var query = new GameSessionQuery();
query.AddParam("map", "SampleMap");
var optionalParameters = new QueryGameSessionOptionalParameters()
{
Limit = 20,
Offset = 0,
Availability = QueryGameSessionAvailability.All
};
session.QueryGameSession(query, optionalParameters, result =>
{
if (result.IsError)
{
// Do something if QueryGameSession has an error
UnityEngine.Debug.Log($"Error QueryGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if QueryGameSession succeeds
});
var response = sdk.Session.GameSession.PublicQueryGameSessionsByAttributesOp
.Execute(new Dictionary<string, object>()
{
{ "map", "SampleMap" }
}, sdk.Namespace);
if (response != null)
{
//do something if sessions are retrieved
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
body := map[string]interface{}{
"map": "samplemap",
}
namespace := "mygame"
input := &game_session.PublicQueryGameSessionsByAttributesParams{
Body: body,
Namespace: namespace,
}
result, err := gameSessionService.PublicQueryGameSessionsByAttributesShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
ApimodelsGameSessionQueryResponse response;
try {
Map<String, ?> reqBody = Collections.singletonMap("map", "SampleMap");
response = gameSessionWrapper.publicQueryGameSessionsByAttributes(PublicQueryGameSessionsByAttributes.builder()
.namespace("<namespace>")
.body(reqBody)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if sessions are retrieved
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.public_query_game_sessions_by_attributes(
body={
"map": "SampleMap",
},
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
現在のユーザーのゲームセッションの取得
ユーザーのゲームセッションのリストとその情報を取得します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.GetMyGameSessions(
THandler<FAccelByteModelsV2PaginatedGameSessionQueryResult>::CreateLambda(
[&](const FAccelByteModelsV2PaginatedGameSessionQueryResult& Result)
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when the operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
session.GetUserGameSessions(null, null, null, result =>
{
if (result.IsError)
{
// Do something if GetUserGameSessions has an error
Debug.Log($"Error GetUserGameSessions, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if GetUserGameSessions succeeds
});
var response = sdk.Session.GameSession.PublicQueryMyGameSessionsOp
.Execute(sdk.Namespace);
if (response != null)
{
//do something if sessions are retrieved
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
input := &game_session.PublicQueryMyGameSessionsParams{
Namespace: namespace,
}
result, err := gameSessionService.PublicQueryMyGameSessionsShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
ApimodelsGameSessionQueryResponse response;
try {
response = gameSessionWrapper.publicQueryMyGameSessions(PublicQueryMyGameSessions.builder()
.namespace("<namespace>")
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if sessions are retrieved
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.public_query_my_game_sessions(
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションの詳細の取得
ゲームセッションのIDを指定して、特定のゲームセッションの詳細を取得します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.GetGameSessionDetails( SessionId,
THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda(
[&](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something when the operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
session.GetGameSessionDetailsBySessionId(sessionId, result =>
{
if (result.IsError)
{
// Do something if GetGameSessionDetailsBySessionId has an error
Debug.Log($"Error GetGameSessionDetailsBySessionId, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if GetGameSessionDetailsBySessionId has succeeds
});
string sessionId = "<targeted-session-id>";
var response = sdk.Session.GameSession.GetGameSessionOp
.Execute(sdk.Namespace, sessionId);
if (response != null)
{
//do something if session detail is retrieved
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.GetGameSessionParams{
Namespace: namespace,
SessionID: sessionId,
}
result, err := gameSessionService.GetGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
ApimodelsGameSessionResponse response;
try {
response = gameSessionWrapper.getGameSession(GetGameSession.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if session detail is retrieved
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.get_game_session(
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションの更新
特定のゲームセッションからゲームセッションのデータを更新します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->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 the operation succeeds
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
var updateRequest = new SessionV2GameSessionUpdateRequest()
{
joinability = SessionV2Joinability.INVITE_ONLY,
version = 2
};
session.PatchGameSession(sessionId, updateRequest, result =>
{
if (result.IsError)
{
// Do something if PatchGameSession has an error
Debug.Log($"Error PatchGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if PatchGameSession succeeds
});
string sessionId = "<targeted-session-id>";
var response = sdk.Session.GameSession.PatchUpdateGameSessionOp
.Execute(new ApimodelsUpdateGameSessionRequest()
{
Joinability = "INVITE_ONLY",
Version = 2
}, sdk.Namespace, sessionId);
if (response != null)
{
//do something if session is updated
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
joinability := "INVITE_ONLY"
version := int32(2)
body := sessionclientmodels.ApimodelsUpdateGameSessionRequest {
Joinability: &joinability,
Version: &version,
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.PatchUpdateGameSessionParams{
Body: &body,
Namespace: namespace,
SessionID: sessionId,
}
result, err := gameSessionService.PatchUpdateGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
ApimodelsGameSessionResponse response;
try {
ApimodelsUpdateGameSessionRequest reqBody = ApimodelsUpdateGameSessionRequest.builder()
.joinability("INVITE_ONLY")
.version(2)
.build();
response = gameSessionWrapper.patchUpdateGameSession(PatchUpdateGameSession.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.body(reqBody)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if session is updated
}
import accelbyte_py_sdk.api.session as session_service
import accelbyte_py_sdk.api.session.models as session_models
result, error = session_service.patch_update_game_session(
body=session_models.ApimodelsUpdateGameSessionRequest()
.with_joinability("INVITE_ONLY")
.with_version(2),
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションの削除
既存のゲームセッションを削除します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.DeleteGameSession(SessionId, FVoidHandler::CreateLambda([]
{
// Successfully deleted game session
}),
FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& Message)
{
// Error deleting game session
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
session.DeleteGameSession(sessionId, result =>
{
if (result.IsError)
{
// Do something if DeleteGameSession has an error
Debug.Log($"Error DeleteGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if DeleteGameSession succeeds
});
string sessionId = "<targeted-session-id>";
sdk.Session.GameSession.DeleteGameSessionOp
.Execute(sdk.Namespace, sessionId);
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.DeleteGameSessionParams{
Namespace: namespace,
SessionID: sessionId,
}
err := gameSessionService.DeleteGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
try {
response = gameSessionWrapper.deleteGameSession(DeleteGameSession.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something when game session is deleted successfully
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.delete_game_session(
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
プレイヤーのゲームセッションへの招待
ユーザーIDを指定して、他のプレイヤーをゲームセッションに招待します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.SendGameSessionInvite(SessionId, UserIdToInvite, FVoidHandler::CreateLambda(
[&]
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// Do something when the operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
var userIdToInvite = "targeted-user-id";
session.InviteUserToGameSession(sessionId, userIdToInvite, result =>
{
if (result.IsError)
{
// Do something if InviteUserToGameSession has an error
Debug.Log($"Error InviteUserToGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if InviteUserToGameSession succeeds
});
string sessionId = "<targeted-session-id>";
string userIdToInvite = "<user-id>";
sdk.Session.GameSession.PublicGameSessionInviteOp
.Execute(new ApimodelsSessionInviteRequest()
{
UserID = userIdToInvite
}, sdk.Namespace, sessionId);
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
userId := "myuserid"
body := sessionclientmodels.ApimodelsSessionInviteRequest {
UserID: &userId,
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.PublicGameSessionInviteParams{
Body: &body,
Namespace: namespace,
SessionID: sessionId,
}
err := gameSessionService.PublicGameSessionInviteShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
String userIdToInvite = "<user-id>";
try {
gameSessionWrapper.publicGameSessionInvite(PublicGameSessionInvite.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.body(ApimodelsSessionInviteRequest.builder().userID(userIdToInvite).build())
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if game session invite is successful
}
import accelbyte_py_sdk.api.session as session_service
import accelbyte_py_sdk.api.session.models as session_models
result, error = session_service.public_game_session_invite(
body=session_models.ApimodelsSessionInviteRequest()
.with_platform_id("PlatformId")
.with_user_id("OtherUserId"),
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
送信したゲームセッション招待のキャンセル
プレイヤーは送信した招待をキャンセルできます。招待された側と招待した側は、招待がキャンセルされたという通知を受け取ります。招待された側は、キャンセル後に以前の招待を受け入れることはできません。
- OSS
// Invitee and inviter listen for session invite canceled notification.
auto OnSessionInviteCanceledDelegate = SessionInterface->AddOnSessionInviteCanceledDelegate_Handle(
FOnSessionInviteCanceledDelegate::CreateLambda([](const FString& SessionID) // ID of canceled game session.
{
// Received invitation canceled.
}));
// Game session leader (inviter) cancel invitation.
FName GameSessionName = NAME_GameSession;
auto OnCancelSessionInviteCompleteDelegate = SessionInterface->AddOnCancelSessionInviteCompleteDelegate_Handle(FOnCancelSessionInviteCompleteDelegate::CreateLambda(
[&](const FUniqueNetId& LocalUserId, FName SessionName, const FUniqueNetId& Invitee, const FOnlineError& ErrorInfo)
{
bool bInvitationCanceled = ErrorInfo.bSucceeded;
// Cancel session invitation complete.
}));
SessionInterface->CancelSessionInvite(PlayerIndex, GameSessionName, *InviteeUniqueNetId);
ゲームセッションへの参加
セッションIDを指定して、既存のゲームセッションに参加します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.JoinGameSession(SessionId, THandler<FAccelByteModelsV2GameSession>::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Result)
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
session.JoinGameSession(sessionId, result =>
{
if (result.IsError)
{
// Do something if JoinGameSession has an error
Debug.Log($"Error JoinGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if JoinGameSession succeeds
});
string sessionId = "<targeted-session-id>";
var response = sdk.Session.GameSession.JoinGameSessionOp
.Execute(sdk.Namespace, sessionId);
if (response != null)
{
//do something when join is success
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.JoinGameSessionParams{
Namespace: namespace,
SessionID: sessionId,
}
result, err := gameSessionService.JoinGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
ApimodelsGameSessionResponse response;
try {
response = gameSessionWrapper.joinGameSession(JoinGameSession.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something when join is successful
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.join_game_session(
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
コードによるセッション参加
セッションでコードが有効になっている場合、プレイヤーはセッションコードを使用してセッションに参加できます。これはパーティーセッションとゲームセッションの両方に適用されます。ユーザーがコードでセッションに参加すると、AGSは招待チェックとセッション参加可能性をバイパスします。セッションタイプは次のとおりです。
- OPEN
- INVITE ONLY
- Friends Only (Friends of members、Friends of leader、Friends of friends)
コード経由で参加するユーザーは、既に招待されているかどうかに関係なく、上記の参加可能性を持つセッションに入ることができます。
- Unreal Engine
- Unity
FString SessionCode = TEXT("yourGameSessionCode");
const FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.JoinGameSessionByCode(
SessionCode,
THandler<FAccelByteModelsV2GameSession>::CreateLambda([&](const FAccelByteModelsV2GameSession& Response)
{
// Do something if JoinGameSessionByCode succeeded
})
, FErrorHandler::CreateLambda([&bIsDone](const int32 ErrorCode, const FString& ErrorMessage)
{
// Do something if JoinGameSessionByCode failed
}));
string sessionCode = "yourGameSessionCode";
AccelByteSDK.GetClientRegistry().GetApi().GetSession()
.JoinGameSessionByCode(sessionCode, result =>
{
if (result.IsError)
{
// Do something if JoinGameSessionByCode has an error
Debug.Log($"Error JoinGameSessionByCode, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if JoinGameSessionByCode succeeded
});
ハングセッションの回避
ハングセッションは、ゲームクライアントがプレイヤーがセッションから退出したと予想しているのに、プレイヤーがまだゲームセッションのアクティブメンバーである場合に発生します。この状況は、ゲームがクラッシュしたり強制終了されたりしたときに、セッションから正常に退出しない場合に発生する可能性があります。
これは、バックエンドとゲームクライアント側の両方からのデータの正確性に影響を与える可能性があります。バックエンド側にハングセッションデータが多すぎると、予期しない動作が発生する可能性があります。例えば:
- ゲーム管理者がプレイヤーのアクティブセッション数を一度に1つのセッションのみに設定します。
- バックエンドにハングセッションデータがある場合、プレイヤーがまだ古いセッションに登録されているため、新しいセッションに参加しようとするとエラーが発生します。
AccelByteは、新しいセッションに参加する際にプレイヤーのアクティブセッションを取得し、最初に退出することをお勧めします。これはセッションブラウザフローで役立ちます。
実装例については、Byte Warsのサブシステムの実装 - セッション入門記事を参照してください。
プレイヤーのアクティビティを一度に1つのゲームセッションに制限するには、セッションで自動ゲームセッション退出オプションを有効にします。有効にすると、新しいゲームセッションに参加する際に、アクティブなゲームセッションからプレイヤーを自動的にキックアウトします。これにより、ゲームセッションがパーティーセッションのように動作し、プレイヤーが一度に1つのアクティブなゲームセッションにのみ参加できるようになります。詳細については、セッションテンプレートの設定記事を参照してください。
ゲームセッション招待の拒否
他のプレイヤーのゲームセッションへの招待を拒否します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.RejectGameSessionInvite(GameSessionID, FVoidHandler::CreateLambda(
[&]
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
session.RejectGameSessionInvitation(sessionId, result =>
{
if (result.IsError)
{
// Do something if RejectGameSessionInvitation has an error
Debug.Log($"Error RejectGameSessionInvitation, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if RejectGameSessionInvitation succeeds
});
string sessionId = "<targeted-session-id>";
sdk.Session.GameSession.PublicGameSessionRejectOp
.Execute(sdk.Namespace, sessionId);
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.PublicGameSessionRejectParams{
Namespace: namespace,
SessionID: sessionId,
}
err := gameSessionService.PublicGameSessionRejectShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
try {
gameSessionWrapper.publicGameSessionReject(PublicGameSessionReject.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something when game session reject is successful
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.public_game_session_reject(
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションからの退出
ゲームセッションIDを指定して、現在参加しているゲームセッションから退出します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.LeaveGameSession(SessionId, FVoidHandler::CreateLambda(
[&]
{
// Do something when the operation succeeds
}),
FErrorHandler::CreateLambda([&](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "current-session-id";
session.LeaveGameSession(sessionId, result =>
{
if (result.IsError)
{
// Do something if LeaveGameSession has an error
Debug.Log($"Error LeaveGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if LeaveGameSession succeeds
});
string sessionId = "<targeted-session-id>";
sdk.Session.GameSession.LeaveGameSessionOp
.Execute(sdk.Namespace, sessionId);
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.LeaveGameSessionParams{
Namespace: namespace,
SessionID: sessionId,
}
err := gameSessionService.LeaveGameSessionShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
try {
gameSessionWrapper.publicGameSessionReject(PublicGameSessionReject.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
import accelbyte_py_sdk.api.session as session_service
result, error = session_service.leave_game_session(
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションリーダーの昇格
ゲームセッションリーダーは、別のメンバーをゲームセッションリーダーに昇格させることができます。ゲームセッションリーダーは1人だけなので、以前のリーダーは通常のメンバーになります。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
Unrealのコードスニペットは近日追加予定です。
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
var sessionId = "targeted-session-id";
var userIdToPromote = "targeted-user-id";
session.PromoteUserToGameSessionLeader(sessionId, userIdToPromote, result =>
{
if (result.IsError)
{
// Do something if PromoteUserToGameSessionLeader has an error
Debug.Log($"Error PromoteUserToGameSessionLeader, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if PromoteUserToGameSessionLeader succeeds
});
string sessionId = "<targeted-session-id>";
string userIdToPromote = "<user-id>";
var response = sdk.Session.GameSession.PublicPromoteGameSessionLeaderOp
.Execute(new ApimodelsPromoteLeaderRequest()
{
LeaderID = userIdToPromote
}, sdk.Namespace, sessionId);
if (response != null)
{
//do something when promotion is success
}
gameSessionService := &session.GameSessionService{
Client: factory.NewSessionClient(&repository.ConfigRepositoryImpl{}),
TokenRepository: &repository.TokenRepositoryImpl{},
}
leaderId := "myleaderid"
body := sessionclientmodels.ApimodelsPromoteLeaderRequest {
LeaderID: &leaderId,
}
namespace := "mygame"
sessionId := "mysessionid"
input := &game_session.PublicPromoteGameSessionLeaderParams{
Body: &body,
Namespace: namespace,
SessionID: sessionId,
}
result, err := gameSessionService.PublicPromoteGameSessionLeaderShort(input)
final GameSession gameSessionWrapper = new GameSession(sdk);
String sessionId = "<targeted-session-id>";
String userIdToPromote = "<user-id>";
ApimodelsGameSessionResponse response;
try {
ApimodelsPromoteLeaderRequest reqBody = ApimodelsPromoteLeaderRequest.builder()
.leaderID(userIdToPromote)
.build();
response = gameSessionWrapper.publicPromoteGameSessionLeader(PublicPromoteGameSessionLeader.builder()
.namespace("<namespace>")
.sessionId(sessionId)
.body(reqBody)
.build());
} catch (Exception e) {
// Do something when failed
return;
}
if (response == null) {
// Null response from server
} else {
// Do something if PromoteUserToGameSessionLeader has been successful
}
プレイヤーのゲームセッションからのキック
ゲームセッションリーダーまたは専用サーバーは、ゲームセッションからメンバーをキックできます。
- Unreal Engine
- Unity
- Unreal Engine OSS
const FString GameSessionID = TEXT("game-session-id");
const FString PlayerID = TEXT("player-id");
// Game session leader kick a player from a game session
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.KickUserFromGameSession(GameSessionID, PlayerID
, FVoidHandler::CreateLambda([]()
{
// Do something when the operation succeeds
}), FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
// Dedicated server kicks a player from a game session
FServerApiClientPtr ServerApiClient = AccelByteOnlineSubsystemPtr->GetServerApiClient();
ServerApiClient->ServerSession.KickUserFromGameSession(GameSessionID, PlayerID
, FVoidHandler::CreateLambda([]()
{
// Do something when the operation succeeds
}), FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& Message)
{
// Do something when operation fails or has an error
}));
string gameSessionId = "sessionIdUserIsIn";
string userId = "userIdOfUserToKick";
// Game session leader kick a player from a game session
var session = AccelByteSDK.GetClientRegistry().GetApi().GetSession();
session.KickUserFromGameSession(gameSessionId, userId, result =>
{
if (result.IsError)
{
// Do something if KickUserFromGameSession has an error
Debug.Log($"KickUserFromGameSession Error: {result.Error.Code}:{result.Error.Message}");
return;
}
// Do something if KickUserFromGameSession succeeds
})
// Dedicated server kicks a player from a game session
var serverSession = AccelByteSDK.GetServerRegistry().GetApi().GetSession();
serverSession.KickUserFromGameSession(gameSessionId, userId, result =>
{
if (result.IsError)
{
// Do something if KickUserFromGameSession has an error
Debug.Log($"KickUserFromGameSession Error: {result.Error.Code}:{result.Error.Message}");
return;
}
// Do something if KickUserFromGameSession succeeds
})
FOnlineSessionV2AccelBytePtr SessionInterface;
FOnlineSessionV2AccelByte::GetFromWorld(GetWorld(), SessionInterface);
FName GameSessionName = NAME_GameSession;
// Game session leader kicks a player from a game session
if (SessionInterface != nullptr)
{
SessionInterface->KickPlayer(LocalPlayerNetId, GameSessionName, PlayerNetIdToKick
, FOnKickPlayerComplete::CreateLambda([](bool bWasSuccessful, const FUniqueNetId& KickedPlayerId)
{
if (bWasSuccessful)
{
// Player successfully kicked
}
}));
}
// Dedicated server kicks a player from a game session
if (SessionInterface != nullptr)
{
SessionInterface->KickPlayer(FUniqueNetIdAccelByteUser::Invalid(), GameSessionName, PlayerNetIdToKick
, FOnKickPlayerComplete::CreateLambda([](bool bWasSuccessful, const FUniqueNetId& KickedPlayerId)
{
if (bWasSuccessful)
{
// Player successfully kicked
}
}));
}
ゲームセッション通知のリスニング
ゲームセッションからさまざまな更新を受信するためにリッスンできるゲームセッション通知がいくつかあります。これはWebSocket接続を使用して、通知がリアルタイムで受信されることを保証します。
- Unreal Engine
- Unity
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Lobby.SetV2GameSessionInvitedNotifDelegate(Api::Lobby::FV2GameSessionInvitedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserInvitedEvent& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2GameSessionMembersChangedNotifDelegate(Api::Lobby::FV2GameSessionMembersChangedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionMembersChangedEvent& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2GameSessionJoinedNotifDelegate(Api::Lobby::FV2GameSessionJoinedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserJoinedEvent& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2GameSessionRejectedNotifDelegate(Api::Lobby::FV2GameSessionRejectedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserRejectedEvent& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2GameSessionKickedNotifDelegate(Api::Lobby::FV2GameSessionKickedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSessionUserKickedEvent& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2GameSessionUpdatedNotifDelegate(Api::Lobby::FV2GameSessionUpdatedNotif::CreateLambda(
[&](const FAccelByteModelsV2GameSession& Notif)
{
// Do something when a notification is received
}));
ApiClient->Lobby.SetV2DSStatusChangedNotifDelegate(Api::Lobby::FV2DSStatusChangedNotif::CreateLambda(
[&](const FAccelByteModelsV2DSStatusChangedNotif& Notif)
{
// Do something when a notification is received
}));
var lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
// Triggered when a game session is updated
lobby.SessionV2GameSessionUpdated += (Result<SessionV2GameSessionUpdatedNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2GameSessionUpdated has an error
Debug.Log($"Error SessionV2GameSessionUpdated, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2GameSessionUpdated is received
};
// Triggered when a game session member has changed
lobby.SessionV2GameSessionMemberChanged += (Result<SessionV2GameMembersChangedNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2GameSessionMemberChanged has an error
Debug.Log($"Error SessionV2GameSessionMemberChanged, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2GameSessionMemberChanged is received
};
// Triggered when a user joins the game session
lobby.SessionV2UserJoinedGameSession += (Result<SessionV2GameJoinedNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2UserJoinedGameSession has an error
Debug.Log($"Error SessionV2UserJoinedGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2UserJoinedGameSession is received
};
// Triggered when a user is kicked from a game session
lobby.SessionV2UserKickedFromGameSession += (Result<SessionV2GameUserKickedNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2UserKickedFromGameSession has an error
Debug.Log($"Error SessionV2UserKickedFromGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2UserKickedFromGameSession is received
};
// Triggered when a game session invite is rejected
lobby.SessionV2UserRejectedGameSessionInvitation += (Result<SessionV2GameInvitationRejectedNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2UserRejectedGameSessionInvitation has an error
Debug.Log($"Error SessionV2UserRejectedGameSessionInvitation, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2UserRejectedGameSessionInvitation is received
};
// Triggered when a user is invited to a game session
lobby.SessionV2InvitedUserToGameSession += (Result<SessionV2GameInvitationNotification> result) =>
{
if (result.IsError)
{
// Do something if SessionV2InvitedUserToGameSession has an error
Debug.Log($"Error SessionV2InvitedUserToGameSession, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if SessionV2InvitedUserToGameSession is received
};
import accelbyte_py_sdk.api.session as session_service
import accelbyte_py_sdk.api.session.models as session_models
result, error = session_service.public_promote_game_session_leader(
body=session_models.ApimodelsPromoteLeaderRequest()
.with_leader_id("OtherUserId"),
session_id="SessionId",
namespace=namespace, # optional, gets the value from the global instance if unspecified
sdk=sdk, # optional, gets the global instance if unspecified
)
if error:
exit(error)
ゲームセッションの更新
Refresh Sessionメソッドを利用して、ゲームセッションをバックエンドと同期するために更新できます。提供されたSessionNameパラメータは、NAME_GameSessionに対応する場合があります。
- OSS Unreal
SessionInterface->RefreshSession(SessionName, FOnRefreshSessionComplete::CreateLambda([]()
{
// On refresh session complete
}));
アクティブセッションの更新
問題を防ぎ、バックエンドとの適切な同期を確保するために、次の関数を使用して、クライアントでローカルにキャッシュされているすべてのアクティブセッションを更新します。
- OSS Unreal
SessionInterface->RefreshActiveSessions(FOnRefreshActiveSessionsComplete::CreateLambda(
[&](bool bWasSuccessful)
{
// On refresh active sessions complete
}));
切断後の再接続時のプレイヤーセッションの処理
プレイヤーの接続が中断され、セッションからプレイヤーが削除される可能性があるシナリオがあります。セッションからの削除は、ゲームセッションの作成時にセッションテンプレートによって決定され、InactiveTimeout期間に基づいています。
したがって、この特定のプレイヤーに対処し、接続が復元された後のセッションの最新の状態を処理する必要があります。
- OSS Unreal
AGS Unreal Engine Online Subsystem (AB OSS)は、接続確立後にセッションの状態を自動的に更新します。 開発者は、このデリゲートをリッスンして、削除されたセッションを知ることができます。
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld());
if (!ensure(Subsystem != nullptr))
{
return;
}
const FOnlineSessionAccelBytePtr SessionInterface = StaticCastSharedPtr<FOnlineSessionV2AccelByte>(Subsystem->GetSessionInterface());
if (!ensure(SessionInterface.IsValid()))
{
return;
}
SessionInterface->AddAccelByteOnReconnectedRefreshSessionDelegate_Handle(
LocalUserNum,
FAccelByteOnReconnectedRefreshSessionDelegate::CreateLambda(
[&](int32 LocalUserNum, bool bWasSuccess, const TArray<FName>& RemovedSessionNames) {
// do something when local session data updated after reconnected
...
}));