オンラインセッション
概要
Quamtumのオンラインサービスは、Photon共通のオンラインインフラストラクチャ(Photon Realtime)上に構築されています。オンラインセッションへの接続は、通常は3つの接続フェーズを経て行われます。
- カスタム認証:Photonはプレイヤーアカウントを提供しないため、独自またはサードパーティー製の認証プロバイダーを使用して安全にログインし、Photonのカスタム認証を行うことを推奨します。
- ゲームサーバー接続:オンラインシミュレーションを開始する前に、クライアントはPhoton Cloudに接続し、Photon Realtime APIを使用してPhotonのルームに参加する必要があります。
- Quantumシミュレーション開始シーケンス:このフェーズでQuantumシミュレーションの開始・同期を行い、クライアント設定とプレイヤーデータを送信します。
ゲームサーバー接続
Photon Cloudへの接続を確立し、ゲームサーバーへマッチメイキングするための最も簡単な方法は次の通りです。
C#
var connectionArguments = new MatchmakingArguments {
// アプリケーションの情報を含むPhotonAppSettingsです。
PhotonSettings = PhotonServerSettings.Global.AppSettings,
// Photon Cloudに設定するようにリクエストするプラグインです。
PluginName = "QuantumPlugin"
// 明示的にルーム名を指定すると、CanOnlyJoinの設定に基づいてルームの作成/参加が試みられます。RoomNameをnullにすると、ルーム作成時にユニークな名前が付きます。
RoomName = "My Room Name",
// ルームに接続できる最大クライアント数で、大抵のケースでは、Quantumシミュレーションの最大プレイヤー数と同じになります。
MaxPlayers = Input.MAX_COUNT,
// 参加のみを試みるか?ルーム作成もリクエストするか?を設定します。
CanOnlyJoin = false,
// カスタム認証に置き換えたら、ここで明示的にAuthValuesを設定します。
UserId = Guid.NewGuid().ToString(),
};
// Photon Cloudに接続し、引数に基づいてマッチメイキングを行い、最終的にルームへ参加します。
RealtimeClient Client = await MatchmakingExtensions.ConnectToRoomAsync(connectionArguments);
実践的には、ConnectToRoomAsync()
/ReconnectToRoomAsync()
をTry Catch
で囲んでください。または、他のTask
に例外を投げてください。
C#
var client = default(RealtimeClient);
try {
client = await MatchmakingExtensions.ConnectToRoomAsync(connectionArguments);
} catch (Exception e) {
// 何か予期せぬことが発生した場合、
// 詳細なエラー処理を行う必要はまず無いので、一般的なフィードバックとしてログを表示し、再試行を促します。
Debug.LogException(e);
}
ConnectToRoomAsync()
中に投げられる例外の例は次の通りです。
Failed to connect and join with error 'X'
:Photon Cloud接続がエラーで失敗した場合MaxPlayer must be greater or equal than 0
MaxPlayer must be less than 256
PhotonSettings must be set
TaskCanceledException
:AsyncConfig.CancellationToken
でキャンセルがリクエストされた場合
ReconnectToRoomAsync()
中に投げられる例外の例は次の通りです。
AppVersion mismatch
:ReconnectInformation.AppVersion
がPhotonSettings.AppVersion
と異なる場合UserId not set
:ReconnectInformation.UserId
がnull
か空の場合UserId mismatch
:ReconnectInformation
とAuthValues
のUserId
が異なる場合ReconnectInformation timed out
:ReconnectInformation
が古い場合ReconnectInformation missing
:ReconnectInformation
がnull
の場合
AppSettings
Photon Cloudに接続するためには、AppSettings
と呼ばれる設定が必要です。Unityプロジェクトでは、PhotonServerSettings.Instance.AppSettings
からグローバルアセットにアクセスできます。
設定オブジェクトは常にコピーするようにして、UnityエディターのPhotonServerSettings
アセットへ変更が保存されないようにしてください。
C#
var appSettings = new AppSettings(PhotonServerSettings.Global.AppSettings);
Photon AppIdのセットアップについては、Quantum Asteroids チュートリアル - プロジェクトの準備をご覧ください。
MatchmakingArgumentsのオプション
MatchmakingArguments
では、以下のようなオプション引数が利用できます。
詳細はPhoton Realtimeのマッチメイキングガイドをご覧ください。
AsyncConfig | TaskFactory とグローバルのキャンセル対応を含む非同期設定です。null の場合、AsyncConfig.Global が使用されます。 |
NetworkClient | Realtimeのクライアントオブジェクトを指定します。null の場合、マッチメイキング処理中に新しいクライアントオブジェクトが作成されます。 |
ReconnectInformation | NetworkClient インスタンスを失った後(例:アプリケーションの再起動後)の再接続のために使用されます。
QuantumReconnectInformation にはPlayerPrefs などの情報が格納されます。使用例については、Quantumデモメニューをご覧ください。
|
EmptyRoomTtlInSeconds | 最後のクライアントが退出した後、空のPhotonルームが残る時間(秒)です。
内部的には RoomOptions.EmptyRoomTtl が設定されます。 |
PlayerTtlInSeconds | 切断されたクライアントがサーバーで非アクティブになった後、ReconnectAndRejoin() で再接続できる時間(秒)です。
内部的には RoomOptions.PlayerTtl が設定されます。 |
AuthValues | Photonサーバー接続の認証値を指定します。これはカスタム認証と合わせて使用されます。
UserId が設定されると、このフィールドが作成されます。 |
CustomProperties | ルームの(初期の/期待する)ルームプロパティを設定します。マッチメイキングで使用されるプロパティ名はCustomLobbyProperties を使用してください。
内部的には RoomOptions.CustomRoomProperties が設定されます。 |
CustomLobbyProperties | ロビー内のクライアントが利用可能なカスタムルームプロパティです。これによって、CustomProperties キーバリューペアが同一のクライアントのみをマッチングさせます。
内部的には RoomOptions.CustomRoomPropertiesForLobby が設定されます。 |
Lobby | マッチングに使用するロビーです。このタイプはフィルターの適用に影響します。
内部的には EnterRoomArgs.Lobby とJoinRandomRoomArgs.Lobby が設定されます。 |
SqlLobbyFilter | マッチングをフィルタリングするSQLクエリです。デフォルトタイプのロビーでは、かわりにExpectedCustomRoomPropertiesを使用してください。
内部的には JoinRandomRoomArgs.SqlLobbyFilter が設定されます。 |
Ticket | カスタムサーバー署名付きのマッチメイキング用チケットです。
内部的には EnterRoomArgs.Ticket とJoinRandomRoomArgs.Ticket が設定されます。 |
RandomMatchingType | MatchmakingMode はルームの埋め方に影響します。
FillRoom - (デフォルト値)プレイヤーを出来るだけ素早く集めるために(古いルームから最初に)ルームを埋めていきます。SerialMatching - 存在するルーム間でプレイヤーを順次に分散します。フィルターが考慮されますが、フィルターが無い場合は、ルームにプレイヤーが均等に分散します。RandomMatching - 完全にランダムにルームへ参加します。ルームプロパティが一致している必要がある以外は、すべての空きルームが選択される可能性があります。
内部的には JoinRandomRoomArgs.MatchingType が設定されます。 |
ExpectedUsers | このクライアントと一緒にルームに参加する予定のユーザーリストです。MaxPlayers 値でルームのスロットを予約します。
内部的には EnterRoomArgs.ExpectedUser とJoinRandomRoomArgs.ExpectedUsers が設定されます。 |
CustomRoomOptions | 他の引数から構成されるEnterRoomArgs.RoomOptions を完全に置き換えます。 |
IsRoomVisible | null でない場合、RoomOptions.IsVisible の初期値になります。 |
IsRoomOpen | null でない場合、RoomOptions.IsOpen の初期値になります。 |
EnableCrc | 有効にすると、クライアントとサーバーで送信されるすべてのパッケージに、CRCチェックサムが追加されます。このチェックサムによって、転送中に破損したパッケージを検知して無視できます。破損したパッケージは損失したパッケージと同様に、再送が必要になる分だけ遅延が発生し、タイムアウトに繋がる可能性があります。チェックサムの追加による処理のオーバーヘッドは少ないですが、送受信データの完全性を高めます。CRCチェックに失敗して破棄されたパッケージはPhotonPeer.PacketLossByCrc にカウントされます。 |
Quantumシミュレーション開始シーケンス
以下のスニペットは、Quantumのオンラインセッションを開始する基本的な操作を示します。
C#
var sessionRunnerArguments = new SessionRunner.Arguments {
// RunnerFactoryによって、Quantum.RunnerとUnityを結合します
RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory,
// デフォルトのQuantumGameStartParametersを作成します
GameParameters = QuantumRunnerUnityFactory.CreateGameParameters,
// 実行中のセッションへ再接続するためのプレイヤースロットを確保する際に使用される秘密のユーザーIDです
ClientId = Client.UserId,
// プレイヤーのデータです
RuntimeConfig = runtimeConfig,
// QuantumDefaultGlobalでタグ付けされたUnityアセットからロードされるセッション設定です
SessionConfig = QuantumDeterministicSessionConfigAsset.DefaultConfig,
// オンラインセッションのGameModeはMultiplayerにする必要があります
GameMode = DeterministicGameMode.Multiplayer,
// セッションの最大プレイヤー数です。ここでは、コード生成されたQuantumシミュレーションの最大プレイヤー数を使用します。
PlayerCount = Input.MAX_COUNT,
// 接続ロジックとQuantumプロトコルが失敗するタイムアウト時間です
StartGameTimeoutInSeconds = 10,
// Communicatorは、シミュレーション開始後にネットワーク処理を引き継ぎます
Communicator = new QuantumNetworkCommunicator(Client),
};
// クライアントがオンラインセッションへ正常に参加するとメソッドが完了します
QuantumRunner runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments);
プレイヤーの追加/削除
Quantumにはプレイヤーの概念があります。各クライアントは0~複数のプレイヤーを持つことができます。Quantumオンラインセッションを開始してからプレイヤーを明示的に追加するまで、クライアントは観客の状態になります。
ゲームに接続している各プレイヤーには、ユニークIDが割り当てられます。このIDはPlayerRef
と呼ばれ、Player
として参照されます。
Quantum 2.1とは異なり、プレイヤーはいつでも追加/削除できます。
プレイヤーが占有するPlayerSlots
は、クライアントが自身のプレイヤーを管理するために参照されます。ローカルプレイヤースロットが1つのみ使用される場合、そのスロットは0
になります。クライアントが2番目のプレイヤーを制御する場合は、PlayerSlot
が1
になります。典型的な使用例は、入力コールバックCallbackPollInput
で、どのローカルプレイヤー入力をポーリングするかを制御する際(QuantumGame.AddPlayer(Int32 playerSlot, RuntimePlayer data)
)にPlayerSlot
プロパティを使用します。
AddPlayer()
は、開発者のバックエンドへHTTPリクエストを送信する可能性があるため、サーバー側に操作頻度の制限があり、スパムできないようになっています。
RuntimePlayer
Quantumにはプレイヤーオブジェクト/プレイヤーアバターの概念は組み込まれていません。
プレイヤーに関連するゲーム情報(キャラクターの装備・レベルなど)は、各クライアントがRuntimePlayer
オブジェクトを使用してシミュレーションに渡すことになります。
C#
// プレイヤースロット0にプレイヤーを追加する
QuantumRunner.Default.Game.AddPlayer(runtimePlayer);
プレイヤースロットを明確に指定するには、QuantumGame.AddPlayer(int playerSlot, RuntimePlayer data)
を以下の例のように使用します。
C#
// プレイヤースロット1にプレイヤーを追加する
QuantumRunner.Default.Game.AddPlayer(1, runtimePlayer);
PlayerConnectedSystem
Quantumセッションへのプレイヤー接続を追跡するために、入力と接続フラグが使用されます。PlayerConnectedSystem
はこのプロセスを自動化し、プレイヤーのセッションへの接続/切断をシミュレーションに通知します。
接続/切断のコールバックを受け取るには、システムにISignalOnPlayerConnected
/ISignalOnPlayerDisconnected
を実装する必要があります。
ローカルプレイヤーの便利なコールバック
ローカルプレイヤー処理に便利なコールバックとして、CallbackLocalPlayerAddConfirmed
・CallbackLocalPlayerRemoveConfirmed
・CallbackLocalPlayerAddFailed
・CallbackLocalPlayerRemoveFailed
があります。
C#
QuantumCallback.Subscribe(this, (CallbackLocalPlayerAddConfirmed c) => OnLocalPlayerAddConfirmed(c));
QuantumCallback.Subscribe(this, (CallbackLocalPlayerRemoveConfirmed c) => OnLocalPlayerRemoveConfirmed(c));
QuantumCallback.Subscribe(this, (CallbackLocalPlayerAddFailed c) => OnLocalPlayerAddFailed(c));
QuantumCallback.Subscribe(this, (CallbackLocalPlayerRemoveFailed c) => OnLocalPlayerRemoveFailed(c));
セッションの停止/切断
Quantumシミュレーションを停止するには、QuantumRunner.ShutdownAll(bool immediate)
を実行します。Quantumコールバック内で呼び出されていない場合のみ、immediate:true
が設定できます。Shutdown
コマンドがQuantumコールバック内で呼び出されている場合は、immediate:false
の設定が必須で、次のUnity更新までシャットダウンが延期されます。
ShutdownAll
はQuantumRunner
オブジェクトを破棄し、Quantumローカルシミュレーションの停止がトリガーされます。StartParameters.QuitBehaviour
の設定値によって、Disconnect()
/LeaveRoom()
のいずれかが実行されます。
クライアントがゲームを正常に終了する(例:プレイヤーアバターを後片付けする)場合は、シミュレーションで追加のロジックを実装する必要があります。これには、クライアントが発行したコマンドか、プレイヤー接続状態(PlayerConnectedSystem
を参照)の監視を行います。
プレイヤーがアプリを閉じたり、ゲームを「Alt+F4」で終了したりすることを考慮すると、正常に切断できる機会が常にあるとは限りません。
C#
async void Disconnect() {
// すべてのランナーをシャットダウンし、切断するまで待機する
await QuantumRunner.ShutdownAllAsync();
// または、単純にシャットダウンする
QuantumRunner.ShutdownAll();
}
プラグイン切断エラー
クライアント開始プロトコルや入力メッセージでQuantumプラグインにエラーが発生した場合、接続を正常に終了する命令が送信されます。その場合、クライアント上でCallbackPluginDisconnect
コールバックが呼び出され、詳細を含むReason
文字列が取得できます。これらのエラーは復帰不可能のため、クライアントは再接続とシミュレーションの再起動が必要です。
Error #3 | Must request start before any protocol message or input messages other than 'StartRequest' is accepted | クライアントが開始リクエストを送信する前に、プロトコルや入力メッセージを送信しようとしました。 |
Error #5 | Duplicate client id | クライアントが既に使用されているClientId でゲームを開始しようとしました。 |
Error #7 | Client protocol version '2.2.0.0' is not matching server protocol version '3.0.0.0' | クライアントが互換性のないバージョンのプロトコルでゲームを開始しようとしました。 |
Error #8 | Invalid client id 'NULL' | クライアントがClientId を指定せずにゲームを開始しようとしました。 |
Error #9 | Server refused client | カスタムプラグインがクライアントのセッション参加を拒否しました。 |
Error #12 | Operation not allowed when running in spectating mode | クライアントが観客モード(プレイヤーを追加していない状態)でコマンドを送信しようとしました。 |
Error #13 | Snapshot request failed to start | クライアントがゲームに途中参加したものの、参加するためのスナップショットを提供する適切なプレイヤーが存在しませんでした。 |
Error #16 | Player corrupted | プロトコルまたは入力メッセージのデシリアライズ中に、クライアントがプラグイン上で例外を起こしました。このエラーは、クライアント接続でパケットのCRCチェックサムを有効(RealtimeClient.RealtimePeer.CrcEnabled = true )にすることで処置できます。 |
Error #17 | PlayerCount is not valid | クライアントが無効なプレイヤー数でオンラインのゲームを開始しました。 |
Error #19 | Player not found | クライアントが自身が所有していないPlayerSlot へコマンドを送信しました。 |
Error #20 | RPC data corrupted | プレイヤー追加時のRuntimePlayer オブジェクトまたはコマンドデータが大きすぎます(最大24KB)。 |
Error #21 | Quantum SDK 2 not supported on Quantum 3 AppIds, check Photon dashboard to set correct version | Quantum SDK 2.1を使用しているクライアントがQuantum 3 AppIdで接続しようとしました。 |
Error #22 | Tick violation, type=RPC, requested=694, current=93 | サーバーが過剰に入力やコマンドを送信しているクライアントを検知して切断しました。これは、クライアントが非常に遠い未来の入力を送信していたり、長時間に渡って大量のコマンドを送信していたりする場合に起こります。 |
Error #30 | Create game failed Exception Message |
ゲーム作成中にクライアントで例外が発生しました。 |
Error #31 | Create game failed Webhook Error Message |
CreateGameのWebhookが失敗しました。 |
Error #32 | Join game failed Webhook Error Message |
JoinGameのWebhookが失敗しました。 |
Error #33 | Player data was rejected Webhook Error Message |
AddPlayerのWebhookが失敗しました。 |
Error #34 | Game configs not loaded Webhook Error Message |
ゲーム設定のWebhookが失敗して、すべてのクライアントを切断します。 |
Error #40 | Caught exception receiving protocol messages | クライアントがプロトコルメッセージを処理する際に例外が発生しました。これは必ずしもサーバーエラーではありませんが、クライアントの状態は復帰不可能で切断されます。 |
Error #41 | Input cache full | クライアントのローカルのデルタ圧縮入力バッファが満杯です。これは、長時間のブレークポイントの停止で発生する可能性があり、クライアントの状態は復帰不可能です。 |
Error #42 | Communicator not connected | シミュレーション実行中に接続が失われました。Photon Realtimeコールバックを使用して、早期に切断を検出することでこれを防ぐことができます。 |
Error #51 | Snapshot download timeout | デフォルトのタイムアウト(20秒)以内に、サーバーがすべての必要なスナップショットを送信できませんでした。 |
Error #52 | Snapshot upload timeout | デフォルトのタイムアウト(10秒)以内に、要求されたバディスナップショットがアップロードできませんでした。 |
Error #53 | Snapshot upload error | アップロードされたバディスナップショットにエラーが含まれていました。 |
Error #54 | Snapshot upload disconnected | バディスナップショットをアップロードするクライアントが切断されたため、途中参加が中断されました。 |