SDK で Lobby を設定する
概要
AccelByte Gaming Services (AGS) Lobby は、WebSocket プロトコルを使用して、ゲームとプレイヤー間の継続的な接続を提供します。WebSocket はサーバーとクライアント間の双方向通信を可能にすることで、信頼性の高いリアルタイムのデータ転送を確保します。AGS Lobby はゲームの主要ハブであるため、他の多くの AGS 機能と接続されています。
この記事では、AGS Game SDK を使用した Lobby の設定について説明します。
目標
この記事の目標は、AGS Game SDK を使用して以下を行う方法を示すことです:
- AGS Lobby への接続。
- 接続および切断イベントの処理。
- ユーザープレゼンスの処理。
- 通知のリスン。
前提条件
この記事の手順を完了するには、以下が必要です:
- プレイヤーが AGS Lobby に到達する前に発生する認証フローへの理解。
- AGS 管理者ポータルへのアクセス。
- Unreal または Unity プロジェクトにインストールされた AGS Game SDK と、ゲームクライアントの以下の認証情報:
- クライアント ID
- クライアントシークレット
Lobby への接続
このセクションでは、ユーザーを Lobby に接続する方法を説明します。
- OSS
- Unreal Engine
- Unity
AGS Online Subsystem (OSS) で Lobby に接続する方法は 2 つあります:
[OnlineSubsystemAccelByte]セクションのDefaultEngine.iniでbAutoLobbyConnectAfterLoginSuccessをtrueに設定します。設定すると、OSS はユーザーのログインが成功したときに Lobby への接続を試みます。- 自動的に接続されない場合は、
FOnlineIdentityAccelByte::ConnectAccelByteLobbyを呼び出すことができます。
// Manually connect to Lobby
int32 LocalUserNum = 0;
FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(OnlineSubsystem->GetIdentityInterface());
AB_TEST_TRUE(IdentityInterface.IsValid());
IdentityInterface->ConnectAccelByteLobby(LocalUserNum);
ユーザーを Lobby に接続するには以下のコードを使用します:
auto ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
auto LobbyApi = ApiClient->GetLobbyApi().Pin();
LobbyApi->Connect();
認証成功後すぐに Lobby サービスに接続することを強くお勧めします。この方法により、競合状態を防ぎ、正確な CCU メトリクスの追跡を確保できます。Lobby 接続を確立するには以下のコードを使用します:
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().Connect();
Lobby 接続を設定したら、プレゼンス、パーティ、フレンズ、チャットなどの機能を追加できます。
Lobby からの切断
このセクションでは、ユーザーを Lobby サービスから切断する方法とベストプラクティスについて説明します。
- Unity
ユーザーをログアウトする場合や、ゲームクライアントが終了またはクラッシュする場合にのみ切断してください。これらの場合は、以下のコードを使用します:
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
void Logout()
{
user.Logout(logoutResult =>
{
if (logoutResult.IsError)
{
// Handle logout error here
return;
}
lobby.Disconnect();
});
}
void OnApplicationQuit()
{
lobby.Disconnect();
}
接続および切断イベントの処理
-
トラブルシューティングの目的で、プレイヤーが自発的にゲームを終了するとき(メニューから、Alt+F4 を使用して、ゲームコンソールをオフにするなど)に切断関数を呼び出してください。予期しないシャットダウン(ゲームのクラッシュ、停電など)では関数を呼び出す必要はありません。
-
開発者環境で
Disconnectedコールバックのコードを調べることができます(例:https://prod.gamingservices.accelbyte.io/lobby/v1/messages)。 -
SDK が Lobby に自動再接続するときに
SetReconnectingDelegateがトリガーされます。 -
自動再接続がタイムアウトすると、
ConnectionClosedが次のステータスコードでトリガーされます:
EWebsocketErrorTypes::DisconnectFromExternalReconnect (4042)
Reconnection total timeout limit reached
この機能は、Lobby サーバー接続の変更をゲームに通知します。SDK はすでに自動再接続メカニズムを提供していますが、自動再接続がタイムアウトした場合は手動で再接続する必要があります。
- OSS
- Unreal Engine
- Unity
OSS では、ユーザーがログアウトすると Lobby は自動的に切断されます。
Lobby 接続イベントの通知を受け取るために、いくつかのデリゲートを追加できます。
デリゲートにアクセスするには、FOnlineIdentityAccelBytePtr 型の AGS Identity Interface を取得する必要があります。以下のコードを使用します:
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld());
if (!ensure(Subsystem != nullptr))
{
return;
}
const FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->GetIdentityInterface());
if (!ensure(IdentityInterface.IsValid()))
{
return;
}
使用できるデリゲートは以下の通りです:
-
Lobby に接続: このデリゲートは Lobby への接続が成功したときにトリガーされます。
auto Handle = IdentityInterface->AddAccelByteOnConnectLobbyCompleteDelegate_Handle(UserNum, FAccelByteOnConnectLobbyCompleteDelegate::CreateLambda(
[&](int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FOnlineErrorAccelByte& Error)
{
if(bWasSuccessful)
{
// Handle successful lobby connection
}
else
{
// Handle failed connection from Error field
}
})); -
Lobby 接続がクローズ: このデリゲートは Lobby 接続が失敗するか、再接続に失敗した場合にトリガーされます。
auto Handle = IdentityInterface->AddAccelByteOnLobbyConnectionClosedDelegate_Handle(UserNum, FAccelByteOnLobbyConnectionClosedDelegate::CreateLambda(
[&](const int32 LocalUserNum, const FUniqueNetId& UserId, const int32 StatusCode, const FString& Reason, const bool bWasClean)
{
// Handle failed lobby connection -
Lobby 再接続中: このデリゲートは、SDK が自動再接続を試みている間に Lobby 接続が一時的に切断されたときにトリガーされます。デフォルトでは、再接続は 60 秒以内に完了します。
auto Handle = IdentityInterface->AddAccelByteOnLobbyReconnectingDelegate_Handle(UserNum, FAccelByteOnLobbyReconnectingDelegate::CreateLambda(
[&](const int32 LocalUserNum, const FUniqueNetId& UserId, const int32 StatusCode, const FString& Reason, const bool bWasClean)
{
// Handle lobby connection when temporarily disconnected
})); -
Lobby 再接続済み: このデリゲートは Lobby への再接続が成功したときにトリガーされます。
auto Handle = IdentityInterface->AddAccelByteOnLobbyReconnectedDelegate_Handle(UserNum, FAccelByteOnLobbyReconnectedDelegate::CreateLambda(
[&]
{
// Handle lobby connection when successfully reconnected
}));
OSS では、ユーザーがログアウトすると Lobby は自動的に切断されます。
auto LobbyApi = ApiClient->GetLobbyApi().Pin();
LobbyApi->Connect();
LobbyApi->SetConnectSuccessDelegate(AccelByte::Api::Lobby::ConnectSuccess.CreateLambda([]()
{
// Do something when ConnectSuccessDelegate succeeds
}));
LobbyApi->SetConnectFailedDelegate(AccelByte::Api::Lobby::ConnectError.CreateLambda([]()
{
// Do something when ConnectFailedDelegate succeeds
}));
LobbyApi->SetConnectionClosedDelegate(AccelByte::Api::Lobby::ConnectionClosed.CreateLambda([]()
{
// Do something when ConnectionClosedDelegate succeeds
}));
var lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
lobby.Connect();
lobby.Connected += () =>
{
// Do something when Lobby connects
};
lobby.Disconnected += error =>
{
// Do something when Lobby disconnects
Debug.Log($"Lobby Disconnected, Error Code: {(int) error}");
};
プレゼンス
AGS プレゼンスは AGS ソーシャルの一部です。プレゼンスはユーザープレゼンスのステータス(アベイラビリティとアクティビティ)を処理します。
アベイラビリティステータスは、オンラインになったときにユーザーがどのように見られたいかです(例: オンライン、オフライン、退席中など)。アクティビティステータスは、ユーザーの現在のアクティビティを表示するために使用できる任意の文字列値です(例: アイドル、ゲームをプレイ中、ロビー内、マッチメイキング中、マッチ中など)。AGS プレゼンスはオンラインのフレンドをリストアップし、プレゼンスステータスの変化を取得するためのフィルターとして AGS フレンズに依存します。
AGS プレゼンスフロー:
- ユーザーが Lobby との接続を初期化した後、すべてのフレンドのプレゼンスステータスを取得します。
- ユーザーがアクティビティを変更するたびにユーザーステータスを設定します。
- フレンドのプレゼンスステータスの更新をリスンし、フレンドのプレゼンスステータスを更新します。
- ユーザーが Lobby サーバーから切断されると、ユーザーのアベイラビリティは自動的に 0 にリセットされます。
- ユーザーが Lobby サーバーから切断された場合、ユーザーのアクティビティは保持されます。
アベイラビリティは列挙型の整数として表されます:
- 0 : オフライン
- 1 : 利用可能
- 2 : ビジー
- 3 : 非表示
アクティビティは任意の文字列として設定できます。例:
- "Playing Survival"
- "In Lobby"
- "In Match"
ステータスの作成とフレンドのステータスの表示
プレイヤーのプレゼンスは、ゲーム、プレイヤーポータル、またはランチャーによって設定できます。これにより、プレイヤーのフレンドがプレイヤーがオンラインであることや何をしているかを確認できます。
- OSS
- Unreal Engine
- Unity
OnlineSubsystem = static_cast<FOnlineSubsystemAccelByte*>(IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM));
PresenceInterface = OnlineSubsystem->GetPresenceInterface();
// Listen for friend presence change notifications
auto OnPresenceReceived = PresenceInterface->AddOnPresenceReceivedDelegate_Handle(FOnPresenceReceivedDelegate::CreateLambda([](const class FUniqueNetId& UserId, const TSharedRef<FOnlineUserPresence>& Presence)
{
// Do something when a friend presence changes
}));
// Create or change a status
PresenceInterface->SetPresence(*CurrentUser.Get(), Status, IOnlinePresence::FOnPresenceTaskCompleteDelegate::CreateLambda([](const FUniqueNetId& User, bool bWasSuccessful)
{
// Do something when a presence is successfully set
}));
auto LobbyApi = ApiClient->GetLobbyApi().Pin();
// View Changed Friend's Status
LobbyApi->SetUserPresenceNotifDelegate(THandler<FAccelByteModelsUsersPresenceNotice>::CreateLambda([](const FAccelByteModelsUsersPresenceNotice Result)
{
UE_LOG(LogTemp, Log, TEXT("Friend %s, Activity: %s | Availability: %s "), Result.UserID, Result.Activity, Result.Availability);
}
));
// Create or change a status
LobbyApi->SetUserPresenceResponseDelegate(Api::Lobby::FSetUserPresenceResponse::CreateLambda([](FAccelByteModelsSetOnlineUsersResponse result)
{
UE_LOG(LogTemp, Log, TEXT("User Status Changed!"));
}));
FString UserActivity = TEXT("Playing Game");
LobbyApi->SendSetPresenceStatus(Availability::Busy, UserActivity);
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().FriendsStatusChanged += result =>
{
if (result.IsError)
{
// Do something when an error is received from FriendsStatusChanged
Debug.Log(result.Error.Message);
return;
}
// Do something when FriendsStatusChanged succeeds
Debug.Log("Friend " + result.Value.userID + ", Activity: " + result.Value.activity + " | Availability: " + result.Value.availability);
};
// Create or change a status
string userActivity = "Playing Game";
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().SetUserStatus(
UserStatus.Busy,
userActivity,
result =>
{
if (result.IsError)
{
// Do something when SetUserStatus fails
Debug.Log(result.Error.Message);
return;
}
// Do something when SetUserStatus succeeds
Debug.Log("User Status Changed!");
}
);
フレンドのステータスリストを取得する
プレイヤーはすべてのフレンドのステータスのリストを見ることもできます。
- Unreal Engine
- OSS
- Unity
auto ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
LobbyApi->SetGetAllUserPresenceResponseDelegate(Api::Lobby::FGetAllFriendsStatusResponse::CreateLambda([](FAccelByteModelsGetOnlineUsersResponse result)
{
for(int i = 0; i < Result.friendId.Num(); i++)
{
UE_LOG(LogTemp, Log, TEXT("Friend %s, Activity: %s | Availability: %s "), Result.friendId[i], Result.Activity[i], Result.Availability[i]);
}
}));
LobbyApi->SendGetOnlineUsersRequest();
この機能は OSS では利用できません。
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().ListFriendsStatus(result =>
{
if (result.IsError)
{
// Do something when ListFriendsStatus fails
Debug.Log(result.Error.Message);
return;
}
// Do something when ListFriendsStatus succeeds
for (int i = 0; i < result.Value.friendsId.Length; i++)
{
Debug.Log("Friend " + result.Value.friendsId[i] + ", Activity: " + result.Value.activity[i] + " | Availability: " + result.Value.availability[i]);
}
});
すべてのフレンドのステータスが返されない場合があります。フレンドのステータスが見つからない場合は、ゲームの実装でアベイラビリティをオフラインに設定する必要があります。
一括フレンドプレゼンス
すべてのプレイヤーのプレゼンス情報を一括で取得できます。これにより、プレゼンスステータス(オンライン、ビジー、非表示、オフラインなど)に基づいてユーザー数もカウントされます。countOnly パラメーターを true に設定して、ユーザーのアカウントデータを取得せずにカウントのみを取得することもできます。
- OSS
- Unreal Engine
- Unity
OnlineSubsystem = static_cast<FOnlineSubsystemAccelByte*>(IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM));
PresenceInterface = OnlineSubsystem->GetPresenceInterface();
auto OnPresenceReceived = PresenceInterface->AddOnPresenceReceivedDelegate_Handle(FOnPresenceReceivedDelegate::CreateLambda([](const class FUniqueNetId& UserId, const TSharedRef<FOnlineUserPresence>& Presence)
{
// Do something when querying a user presence completes
}));
PresenceInterface->QueryPresence(UserId);
TArray<FString> UserIds = {FString("12345abcd"), FString("abcd12345")};
bool CountOnly = true;
auto ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
LobbyApi->Connect();
LobbyApi->BulkGetUserPresence(UserIds, THandler<FAccelByteModelsBulkUserStatusNotif>::CreateLambda([](const FAccelByteModelsBulkUserStatusNotif& Result)
{
// Do something if BulkGetUserPresence succeeds
}), FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& ErrorMessage)
{
// Do something if BulkGetUserPresence has an error
UE_LOG(LogTemp, Log, TEXT("Error BulkGetUserPresence, Error Code: %d Error Message: %s"), ErrorCode, *ErrorMessage);
}), CountOnly);
var userIds = new List<string>();
userIds.Add("123456789");
userIds.Add("987654321");
bool countOnly = false;
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BulkGetUserPresence(userIds.ToArray(), result =>
{
if (result.IsError)
{
// Do something when BulkGetUserPresence fails
Debug.Log("Error: " + result.Error.Code + " | Message: " + result.Error.Message);
return;
}
// Do something when the BulkGetUserPresence succeeds
foreach (var user in result.Value.data)
{
Debug.Log("UserId: " + user.userID + " | Availability: " + user.availability.ToString());
}
Debug.Log("User's status count. Online: " + result.Value.online + " | Busy:" + result.Value.busy + " | Invisible:" + result.Value.invisible + " | Offline:" + result.Value.offline);
}, countOnly);
通知
通知に関するいくつかのタスクがあります:
フリーフォーム通知の送信
- 通知 REST API エンドポイントを呼び出して任意のテキスト通知メッセージを送信します。
- 通知クライアントが新しい通知をリスンします。
テンプレート化された通知の送信
- 管理者がテンプレートスラッグ(識別子)、コンテキスト(置き換えるテンプレートの値)、テンプレート言語を使用して通知テンプレートを作成します。
- 管理者がテンプレートを公開します。
- テンプレートスラッグ、コンテキスト、言語がサービスコンシューマー(管理者ユーザーまたは他のサービス)に送信されます。
- サービスコンシューマーがテンプレートスラッグ、言語、テンプレートコンテキストの値を指定して通知 REST API エンドポイントを呼び出します。
- 通知クライアントが新しい通知をリスンします。
非同期通知の送信
- フリーフォームまたはテンプレート化された通知のワークフローに従います。
- REST API エンドポイントを呼び出すときに、クエリパラメーターを非同期として指定します。
- プレイヤーがオンラインの場合、メッセージは即座に送信されます。
- プレイヤーがオフラインの場合、メッセージは保存されます。
保存された通知の取得
- プレイヤーが Lobby サーバーに接続します。
- 通知はプレイヤーに送信できます。
ユーザーに送信されるすべての通知は一般的な形式です。クライアントは、通知に設定されたトピックに基づいて通知の配置場所を決定できます。たとえば、ゲーム更新の通知はポップアップとして表示でき、ゲームサーバーの通知はシステムチャットボックスに表示できます。
WebSocket 通知形式
通知メッセージ:
type: messageNotif
topic: updateNotification
from: system
to: user123
payload: message content 123
sentAt: 2018-11-25T23:45:05Z
同期通知の取得
SDK を使用して同期通知を取得するには、通知デリゲートを追加する必要があります。
- Unreal Engine
- OSS
- Unity
const auto NotificationDelegate = AccelByte::Api::Lobby::FMessageNotif::CreateLambda([](const FAccelByteModelsNotificationMessage& Result)
{
UE_LOG(LogTemp, Log, TEXT("There is an incoming notification."));
UE_LOG(LogTemp, Log, TEXT("From: %s \nTo: %s\nTopic: %s"), *Result.From, *Result.To, *Result.Topic);
UE_LOG(LogTemp, Log, TEXT("Notification: %s"), *Result.Payload);
});
auto ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
LobbyApi->SetMessageNotifDelegate(NotificationDelegate);
LobbyApi->Connect();
この機能は OSS では利用できません。
public static void OnReceiveNotification(Result<Models.Notification> result)
{
if (result.IsError)
{
// Do something when OnReceiveNotification fails
Debug.Log(result.Error.Message);
return;
}
// Do something when OnReceiveNotification succeeds
Debug.Log(result.IsError);
Debug.Log("There is an incoming notification.");
Debug.Log(result.Value.payload);
}
public static void Main(string[] args)
{
var lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
lobby.OnNotification += OnReceiveNotification;
lobby.Connect();
}
トピックで通知をフィルタリングすることもできます:
- Unreal Engine
- OSS
- Unity
const auto NotificationDelegate = AccelByte::Api::Lobby::FMessageNotif::CreateLambda([](const FAccelByteModelsNotificationMessage& Result)
{
UE_LOG(LogTemp, Log, TEXT("There is an incoming notification."));
if(Result.Topic == "INGAME")
{
UE_LOG(LogTemp, Log, TEXT("Game notification: %s"), *result.Payload);
}
Else if(Result.Topic == "EVENT")
{
UE_LOG(LogTemp, Log, TEXT("Event notification: %s"), *result.Payload);
}
});
この機能は OSS では利用できません。
public static void OnReceiveNotification(Result<Models.Notification> result)
{
if (result.IsError)
{
// Do something when OnReceiveNotification fails
Debug.Log(result.Error.Message);
return;
}
// Do something when OnReceiveNotification succeeds
Debug.Log("There is an incoming notification.");
switch (result.Value.topic)
{
case "INGAME" : Debug.Log("Game notification: " +result.Value.payload); break;
case "EVENT" : Debug.Log("Event notification: " +result.Value.payload); break;
}
}