SDKを使用してマッチセッションを作成する - 専用サーバーで参加可能なセッションを作成する - (Unityモジュール)
注釈:本資料はAI技術を用いて翻訳されています。
ラッパーを展開する
このチュートリアルでは、AccelByte Gaming Services (AGS) Software Development Kit (SDK) を使用してAGSマッチメイキングを実装する方法を学びます。Byte Warsでは、ゲームインスタンスラッパーがMatchSessionDSWrapperクラスで定義されています。このラッパーは、ゲームセッションの作成や閲覧などのゲームセッション関連のアクションを管理します。このチュートリアルでは、MatchSessionDSWrapperクラスのスターターバージョンであるMatchSessionDSWrapper_Starterクラスを使用します。
スターターパックの内容
このチュートリアルを進めるには、MatchSessionDSWrapper_Starterというスターターラッパークラスを使用します。このクラスは、以下のファイルで定義されているMatchSessionEssentialsWrapperクラスを継承しています:
- C#ファイル:
Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsWrapper.cs
MatchSessionDSWrapper_Starterクラスは以下のファイルで定義されています:
- C#ファイル:
Assets/Resources/Modules/Play/MatchSessionDS/Scripts/MatchSessionDSWrapper_Starter.cs
さらに、以下のファイルにヘルパー関数と変数を含むモデルクラスが定義されています:
- C#ファイル:
Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSessionModels.cs - C#ファイル:
Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsModels.cs
MatchSessionEssentialsWrapper自体はSessionEssentialsWrapperクラスを継承しており、このクラスには参加、退出、セッション招待の拒否などの基本的なセッション管理機能が含まれています。この機能の実装方法については、前のセッション入門モジュールで学びました。MatchSessionEssentialsWrapperには、以下を含むいくつかの事前定義されたヘルパーコンポーネントが含まれています:
-
ゲームセッションの状態を処理するデリゲート:
public static ResultCallback<SessionV2DsStatusUpdatedNotification> OnDSStatusChanged = delegate { };
MatchSessionEssentialsWrapperのルート親クラスであるAccelByteWarsOnlineSessionクラスにも、以下を含むいくつかの事前定義されたヘルパーコンポーネントが含まれています:
-
ゲームクライアントを専用サーバーに接続するヘルパーメソッド。
public virtual void TravelToDS(SessionV2GameSession session, InGameMode gameMode)
{
SessionV2DsInformation dsInfo = session.dsInformation;
if (dsInfo == null)
{
BytewarsLogger.LogWarning("Failed to travel to dedicated server. Dedicated server information not found.");
return;
}
if (NetworkManager.Singleton.IsListening)
{
BytewarsLogger.LogWarning("Failed to travel to dedicated server. The instance is running as listen server.");
return;
}
string ip = dsInfo.server.ip;
ushort port = (ushort)dsInfo.server.port;
InitialConnectionData initialData = new InitialConnectionData()
{
sessionId = string.Empty,
inGameMode = gameMode,
serverSessionId = session.id,
userId = GameData.CachedPlayerState.PlayerId
};
GameManager.Instance.ShowTravelingLoading(() =>
{
BytewarsLogger.Log("Travel to dedicated server as client.");
GameManager.Instance.StartAsClient(ip, port, initialData);
});
}
AccelByteWarsOnlineSessionModelsファイルには、以下を含むいくつかの事前定義されたヘルパーコンポーネントが含まれています:
-
Admin Portalで設定されたSession Templateに基づいて、セッションテンプレート名を保存する文字列定数:
public const string EliminationDSAMSSessionTemplateName = "unity-elimination-ds-ams";
public const string TeamDeathmatchDSAMSSessionTemplateName = "unity-teamdeathmatch-ds-ams"; -
セッションテンプレート名に基づいて、ゲーム内モードをセッションリクエストモデルにマッピングするヘルパー辞書:
public static readonly Dictionary<InGameMode, Dictionary<GameSessionServerType, SessionV2GameSessionCreateRequest>> SessionCreateRequestModels = new()
{
...
{
InGameMode.CreateMatchElimination, new()
{
...
{
GameSessionServerType.DedicatedServerAMS,
new SessionV2GameSessionCreateRequest()
{
type = SessionConfigurationTemplateType.DS,
joinability = SessionV2Joinability.OPEN,
configurationName = EliminationDSAMSSessionTemplateName,
matchPool = EliminationDSAMSSessionTemplateName,
clientVersion = ClientVersion
}
}
}
},
{
InGameMode.CreateMatchTeamDeathmatch, new()
{
...
{
GameSessionServerType.DedicatedServerAMS,
new SessionV2GameSessionCreateRequest()
{
type = SessionConfigurationTemplateType.DS,
joinability = SessionV2Joinability.OPEN,
configurationName = TeamDeathmatchDSAMSSessionTemplateName,
matchPool = TeamDeathmatchDSAMSSessionTemplateName,
clientVersion = ClientVersion
}
}
}
}
...
}; -
ゲーム内モードでセッションリクエストモデルを取得するヘルパー関数:
public static SessionV2GameSessionCreateRequest GetGameSessionRequestModel(
InGameMode gameMode,
GameSessionServerType serverType)
{
if (!SessionCreateRequestModels.TryGetValue(gameMode, out var matchTypeDict))
{
return null;
}
matchTypeDict.TryGetValue(serverType, out var request);
return request;
}
MatchSessionEssentialsModelsファイルには、以下を含むいくつかの事前定義されたヘルパーコンポーネントが含まれています:
-
サーバータイプに基づいてゲームセッションクエリをフィルタリングするために使用されるカスタム定数。
public static readonly string MatchSessionAttributeKey = "match_session_type";
public static readonly string MatchSessionDSAttributeValue = "unity_match_session_ds";
public static readonly Dictionary<string, object> MatchSessionDSAttribute = new()
{
{ MatchSessionAttributeKey, MatchSessionDSAttributeValue }
}; -
ゲームセッション閲覧結果を構築するヘルパークラス。
public class BrowseSessionModel
{
public SessionV2GameSession Session { get; private set; }
public AccountUserPlatformData Owner { get; private set; }
public int CurrentMemberCount { get; private set; }
public int MaxMemberCount { get; private set; }
public InGameMode GameMode { get; private set; }
public GameSessionServerType ServerType { get; private set; }
public BrowseSessionModel()
{
Session = null;
Owner = null;
CurrentMemberCount = 0;
MaxMemberCount = 0;
GameMode = InGameMode.None;
ServerType = GameSessionServerType.None;
}
public BrowseSessionModel(SessionV2GameSession session, AccountUserPlatformData owner)
{
Session = session;
Owner = owner;
CurrentMemberCount = session.members.Count(member => member.status == SessionV2MemberStatus.JOINED);
MaxMemberCount = session.configuration.maxPlayers;
GameMode = GetGameSessionGameMode(session);
ServerType = GetGameSessionServerType(session);
}
}
public class BrowseSessionResult
{
public readonly Dictionary<string, object> QueryAttribute;
public readonly PaginatedResponse<BrowseSessionModel> Result;
public BrowseSessionResult(
Dictionary<string, object> queryAttribute,
PaginatedResponse<BrowseSessionModel> result)
{
QueryAttribute = queryAttribute;
Result = result;
}
}
マッチセッションを作成する
このセクションでは、AGS SDKを使用して専用サーバーでゲームセッションを作成する方法を説明します。
-
MatchSessionDSWrapper_Starterクラスを開き、以下の関数を作成します。この関数は、いくつかの属性を追加してセッション作成リクエストを充実させます。次に、親クラスの関数を呼び出してセッション作成リクエストを送信します。これは、セッション入門モジュールで学んだ内容です。リクエストが完了すると、専用サーバーが移動する準備ができているかどうかを確認します。以下のコードから、オプションの属性にいくつかの値を追加してリクエストを充実させていることがわかります。これらの属性の機能は次のとおりです:serverName: ローカル専用サーバーでゲームをテストするために、リクエストされたローカルサーバー名を設定するために使用される属性。clientVersion: AMS開発フリートを使用してテストするためのビルド設定名を選択するために使用される属性。requestedRegions: リクエストされたリージョンを設定して、セッションが特定のリージョンのAMS専用サーバーを使用するようにするために使用される属性。members: セッションメンバーを追加するために使用される属性。この場合、パーティーメンバーをゲームセッションに参加させるために使用されます。attributes: ゲームセッションに追加できるカスタム属性リスト。この場合、カスタムMatchSessionDSAttribute属性を使用してサーバータイプでセッションをフィルタリングするために使用されます。
public override void CreateGameSession(
SessionV2GameSessionCreateRequest request,
ResultCallback<SessionV2GameSession> onComplete)
{
// Add local server name.
if (string.IsNullOrEmpty(ConnectionHandler.LocalServerName))
{
request.serverName = ConnectionHandler.LocalServerName;
}
// Add client version
request.clientVersion = AccelByteWarsOnlineSessionModels.ClientVersion;
// Add preferred regions.
string[] preferredRegions =
RegionPreferencesModels.GetEnabledRegions().OrderBy(x => x.Latency).Select(x => x.RegionCode).ToArray();
if (preferredRegions.Length > 0)
{
request.requestedRegions = preferredRegions;
}
// Add party session id for playing with party feature.
if (CachedParty != null && !string.IsNullOrEmpty(CachedParty.id))
{
request.teams = new SessionV2TeamData[]
{
new SessionV2TeamData
{
TeamId = CachedParty.id,
userIds = CachedParty.members.Where(m => m.StatusV2 == SessionV2MemberStatus.JOINED).Select(m => m.id).ToArray()
}
};
}
// Add custom attribute as filter for session browser.
request.attributes = MatchSessionDSAttribute;
// Reregister delegate to listen for dedicated server status changed event.
OnDSStatusChanged -= OnDSStatusChangedReceived;
OnDSStatusChanged += OnDSStatusChangedReceived;
base.CreateGameSession(request, (result) =>
{
// If dedicated server is ready, broadcast the dedicated server status changed event.
if (!result.IsError && result.Value.dsInformation.StatusV2 == SessionV2DsStatus.AVAILABLE)
{
OnDSStatusChanged?.Invoke(Result<SessionV2DsStatusUpdatedNotification>.CreateOk(new()
{
session = result.Value,
sessionId = result.Value.id,
error = string.Empty
}));
}
onComplete?.Invoke(result);
});
}
マッチセッションに参加する
このセクションでは、AGS SDKを使用して専用サーバーでゲームセッションに参加する方法を説明します。
-
MatchSessionDSWrapper_Starterクラスを開き、以下の関数を作成します。この関数は、親クラスの関数を呼び出してセッション参加リクエストを送信します。これは、セッション入門モジュールで学んだ内容です。リクエストが完了すると、専用サーバーが移動する準備ができているかどうかを確認します。public override void JoinGameSession(
string sessionId,
ResultCallback<SessionV2GameSession> onComplete)
{
// Reregister delegate to listen for dedicated server status changed event.
OnDSStatusChanged -= OnDSStatusChangedReceived;
OnDSStatusChanged += OnDSStatusChangedReceived;
base.JoinGameSession(sessionId, (result) =>
{
// If dedicated server is ready, broadcast the dedicated server status changed event.
if (!result.IsError && result.Value.dsInformation.StatusV2 == SessionV2DsStatus.AVAILABLE)
{
OnDSStatusChanged?.Invoke(Result<SessionV2DsStatusUpdatedNotification>.CreateOk(new()
{
session = result.Value,
sessionId = result.Value.id,
error = string.Empty
}));
}
onComplete?.Invoke(result);
});
}
マッチセッションを閲覧する
このセクションでは、AGS SDKを使用してゲームセッションをクエリする方法を説明します。
-
MatchSessionDSWrapper_Starterクラスを開き、特定の属性に基づいてゲームセッションをクエリする以下の関数を作成します。この場合、MatchSessionDSAttributeを使用して専用サーバータイプを持つゲームセッションのみをフィルタリングします。ロードする特定のページがある場合は、ページネーションURLを解析してクエリ属性に追加しようとします。最後に、ゲームセッションのクエリを開始し、セッションオーナーの詳細とともに結果を返します。public override void BrowseGameSessions(
string pageUrl,
ResultCallback<BrowseSessionResult> onComplete)
{
// If provided, try parse the pagination URL parameters and add them to the query attributes.
Dictionary<string, object> queryAttributes = new(MatchSessionDSAttribute);
if (!string.IsNullOrEmpty(pageUrl))
{
Dictionary<string, object> queryParams = ParseQuerySessionParams(pageUrl);
if (queryParams == null)
{
BytewarsLogger.LogWarning($"Failed to find game sessions. Pagination URL is invalid.");
onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(ErrorCode.InvalidArgument, InvalidSessionPaginationMessage));
return;
}
queryParams.ToList().ForEach(x => queryAttributes[x.Key] = x.Value);
}
// Query game sessions and filter them based on the custom attributes.
Session.QueryGameSession(queryAttributes, sessionResult =>
{
if (sessionResult.IsError)
{
BytewarsLogger.LogWarning(
$"Failed to query game sessions. " +
$"Error {sessionResult.Error.Code}: {sessionResult.Error.Message}");
onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(sessionResult.Error));
return;
}
BytewarsLogger.Log("Success to query game sessions.");
// Filter based on the enabled preferred regions.
List<SessionV2GameSession> filteredSessions =
RegionPreferencesModels.FilterEnabledRegionGameSession(sessionResult.Value.data.ToList());
// Return immediately if result is empty.
if (filteredSessions.Count <= 0)
{
onComplete?.Invoke(Result<BrowseSessionResult>.CreateOk(
new BrowseSessionResult(
queryAttributes,
new PaginatedResponse<BrowseSessionModel>()
{
data = Array.Empty<BrowseSessionModel>(),
paging = sessionResult.Value.paging
})));
return;
}
// Query the session owner user information.
string[] sessionOwners = filteredSessions.Select(x => x.createdBy).ToArray();
User.GetUserOtherPlatformBasicPublicInfo("ACCELBYTE", sessionOwners, (userResult) =>
{
if (userResult.IsError)
{
BytewarsLogger.LogWarning(
$"Failed to query game sessions. " +
$"Error {userResult.Error.Code}: {userResult.Error.Message}");
onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(userResult.Error));
return;
}
// Construct session results.
List<BrowseSessionModel> result = new();
foreach(SessionV2GameSession session in filteredSessions)
{
AccountUserPlatformData ownerData = userResult.Value.Data.FirstOrDefault(x => x.UserId == session.createdBy);
if (session != null && ownerData != null)
{
result.Add(new(session, ownerData));
}
}
// Return results.
onComplete?.Invoke(Result<BrowseSessionResult>.CreateOk(
new BrowseSessionResult(
queryAttributes,
new PaginatedResponse<BrowseSessionModel>()
{
data = result.ToArray(),
paging = sessionResult.Value.paging
})));
});
});
}
マッチセッションイベントをリッスンする
このセクションでは、ゲームセッションイベントをリッスンする方法を説明します。
-
MatchSessionDSWrapper_Starterクラスを開き、事前定義されたAwake()関数を以下のコードに置き換えます。このコードは、バックエンドがセッションを提供する専用サーバーを見つけたときや、参加可能なゲームセッションを表示するために閲覧セッションメニューが開始されたときなどのイベントをリッスンするためにデリゲートをバインドします。protected override void Awake()
{
base.Awake();
BrowseMatchMenu.OnBrowseDSMatch += BrowseGameSessions;
Lobby.SessionV2DsStatusChanged += (result) => OnDSStatusChanged?.Invoke(result);
} -
次に、専用サーバーのステータスを受信したときに処理する新しい関数を作成します。専用サーバーの準備ができている場合、ゲームクライアントはそこに移動します。それ以外の場合は、ステータスタイプに基づいてエラーをスローします。
private void OnDSStatusChangedReceived(Result<SessionV2DsStatusUpdatedNotification> result)
{
if (result.IsError)
{
OnDSStatusChanged -= OnDSStatusChangedReceived;
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Error {result.Error.Code}: {result.Error.Message}");
return;
}
SessionV2GameSession session = result.Value.session;
SessionV2DsInformation dsInfo = session.dsInformation;
// Check if the requested game mode is supported.
InGameMode requestedGameMode = GetGameSessionGameMode(session);
if (requestedGameMode == InGameMode.None)
{
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Session's game mode is not supported by the game.");
OnDSStatusChanged -= OnDSStatusChangedReceived;
OnDSStatusChanged?.Invoke(
Result<SessionV2DsStatusUpdatedNotification>.
CreateError(ErrorCode.NotAcceptable, InvalidSessionTypeMessage));
return;
}
// Check the dedicated server status.
switch (dsInfo.StatusV2)
{
case SessionV2DsStatus.AVAILABLE:
OnDSStatusChanged -= OnDSStatusChangedReceived;
TravelToDS(session, requestedGameMode);
break;
case SessionV2DsStatus.FAILED_TO_REQUEST:
case SessionV2DsStatus.ENDED:
case SessionV2DsStatus.UNKNOWN:
OnDSStatusChanged -= OnDSStatusChangedReceived;
BytewarsLogger.LogWarning(
$"Failed to handle dedicated server status changed event. " +
$"Session failed to request for dedicated server due to unknown reason.");
break;
default:
BytewarsLogger.Log($"Received dedicated server status change. Status: {dsInfo.StatusV2}");
break;
}
}
リソース
-
このチュートリアルで使用されているファイルは、Unity Byte Wars GitHubリポジトリで入手できます。
- Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSession.cs
- Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSessionModels.cs
- Assets/Resources/Modules/Play/SessionEssentials/Scripts/SessionEssentialsWrapper.cs
- Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsWrapper.cs
- Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsModels.cs
- Assets/Resources/Modules/Play/MatchSessionDS/Scripts/MatchSessionDSWrapper_Starter.cs