This document is about: QUANTUM 3
SWITCH TO

再接続

プレイヤーの追加と削除

Quantum にはプレイヤーの概念があります。各クライアントはゼロまたは複数のプレイヤーを持つことができます。Quantum オンラインセッションを初めて開始すると、クライアントはプレイヤーが明示的に追加されるまで spectator の状態にあります。

ゲームに接続されている各プレイヤーにはユニークな ID が割り当てられます。この ID は PlayerRef と呼ばれ、しばしば Player と呼ばれます。

Quantum 2.1 とは異なり、プレイヤーはいつでも追加および削除できます。

プレイヤーは「PlayerSlots」と呼ばれるスロットを占有し、これは常に1つのクライアントが自分のプレイヤーを管理する方法を示します。ローカルプレイヤースロットが1つのみ使用される場合、それはスロット 0 になります。そのクライアントによって制御される2番目のプレイヤーは、PlayerSlot 1 を持つ可能性があります。典型的な使用例として、入力コールバック CallbackPollInput があり、これが PlayerSlot プロパティを使って、どのローカルプレイヤーの入力がポーリングされるかを制御します:QuantumGame.AddPlayer(Int32 playerSlot, RuntimePlayer data)

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 セッションに接続しているかを追跡するために Input & Connection Flags が使用されます。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 シミュレーションが停止されます。これにより、Disconnect() または LeaveRoom() のいずれかが実行され、StartParameters.QuitBehaviour に設定されている内容に依存します。

クライアントがゲームを正常に終了する場合(たとえば、リモートクライアントのプレイヤーアバターをクリーンアップするため)、シミュレーション内に追加のロジックを実装する必要があります。これは、クライアントが発行したコマンドまたは接続された状態を監視すること(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オブジェクトまたはコマンドデータが大きすぎました(最大 24 KB)。
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 #33 Player data was rejected 'Webhook Error Message' プレイヤー追加の 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 遅延参加がバディスナップショットをアップロード中にクライアントが切断されたため中断されました。

可能なエラー: 認証トークンのタイムアウト

認証チケットは 1 時間後に期限切れになります。Quantum ゲームセッションの過程で、切れる前に自動的に更新されます(Photon Realtime: Encryption | Token Refresh)。ゲームセッションが長い場合や、約 20 分後に再接続するプレイヤーをサポートしたい場合は、このエラーを処理する必要があります。解決策は、デフォルトの接続手順を再起動し、ルームに再参加することです。

C#

public void OnDisconnected(DisconnectCause cause) {
    switch (cause) {
        case DisconnectCause.AuthenticationTicketExpired:
        case DisconnectCause.InvalidAuthentication:
            // デフォルトの接続シーケンスで再起動する
        break;

可能なエラー: 接続がまだ利用できない

もちろん、接続が阻害されていたり、他のエラーが発生したりすることがあります。この場合、IConnectionCallbacks.OnDisconnected(DisconnectCause cause) が呼び出されます。

アプリケーション再起動後の再接続

MatchmakingReconnectInformation オブジェクトは、再参加操作に関連するデータをキャッシュしますが、このデータはアプリケーションを再起動すると失われる可能性があります。

その場合、接続を最初から再開し、同じ UserIdFixedRegion、および AppVersion を再利用する必要があります。マスターサーバーに到達したら、Rejoin() または Join() でルームに再参加します。

接続キャッシュが失われているため、再参加が ErrorCode.JoinFailedFoundActiveJoiner で失敗する可能性があります。これは、サーバーがまだ切断を登録していないためです(10 秒のタイムアウト)。この場合は、再参加が成功するまで再試行する必要があります。

Photon UserId を PlayerPrefs に保存することはもちろん、カスタム認証に置き換えることも可能です。

PlayerPrefs 内にスナップショットを保存およびロードすることも可能です。これはプレイヤー数が非常に少ないゲームには興味深いものかもしれません。PlayerPrefs にバイナリデータを string として保存するには、base64 のエンコーディングおよびデコーディングを使用します。

異なるマスターサーバー

ReconnectAndRejoin()ReconnectToMaster() の両方は、クライアントが再接続するときにこれを有効にするため、以前と異なるマスターサーバーにクライアントが接続されるという例外的な状況を防ぎます。理由は以下の通りです:

  • 1 つのアプリに対して複数のクラスターが存在する
  • マスターサーバーが置き換えられた(ローテーション)
  • 最良のリージョン pings に新しい結果がある

その他の Photon Realtime トピックス

これらの機能は再接続には重要ではありませんが、デモメニューサンプルの一部であるため、ここでカバーしておきます。

最良のリージョン要約

リージョン ping は時々強制されますが、プレイヤーが悪い ping 結果にとどまらないようにするためには、無効化を実装するのが賢明かもしれません。これにより、プレイヤーが悪いまたは誤った結果に永遠にとどまることがないようにします(たとえば、ping がしきい値を超えた場合、毎日 BestRegionSummary をクリアするなど)。また、プレイヤーが世界の他の場所に移動することがあるため、新しい ping が必要になる場合があります。

AppVersion

デモメニューサンプルでは、プレイヤーは QuantumMenuViewSettings を介して AppVersion を選択できます。AppSettings に供給する AppVersion は、同じ AppId に対するプレイヤーベースを別々のグループにまとめます。同じ AppId に接続し、異なる AppVersions を持つプレイヤーは互いに発見することはありません。

これは、複数のゲームバージョンを同時に実行したり、開発中に他のクライアント(異なるコードベースを持ち、ゲームを即座に同期不能にさせる)をゲームに参加させないために有用です。

さらなる読み物

実行中の Quantum ゲームへの再接続

Quantum ClientId

ClientId はクライアントとサーバー間の秘密です。他のクライアントはこれを知ることはありません。これは QuantumRunner を開始する際に渡されます。

C#

var sessionRunnerArguments = new SessionRunner.Arguments {
        ClientId = Client.UserId,
        // 必要な他の引数
      };
var runner = (QuantumRunner)await SessionRunner.StartAsync(sessionRunnerArguments);

新しい Photon ルームのアクターとして参加したか、再参加したかに関係なく、再接続クライアントはその ClientId によって識別され、スロットがその間に他のプレイヤーによって埋められていなければ、以前持っていた同じプレイヤーインデックスに割り当てられます。要するに、プレイヤーは再接続時に 同じ ClientId を使用する必要があります。

Quantum は、同じ ClientId を持つ別の active プレイヤーがルーム内にいる限り、クライアントがセッションを開始することを許可しません。切断タイムアウト(10秒)が待機します:

DISCONNECTED: Error #5: Duplicate client id

これが、ReconnectAndRejoin() が短期的な接続損失から回復するために必要である理由です。

さらなる読み物

Quantum セッションの再起動

切断すると QuantumRunnerDeterministicSessionもはや 使用できなくなり、破棄して再作成する必要があります。

クライアントが Quantum ゲームを実行しているルームに参加したり再参加したりすると、QuantumRunner を再起動する必要があります。シミュレーションは、他のクライアントからスナップショットが届くまで一時停止されます。その後、最新のゲーム時間に同期してキャッチアップします。

大まかな手順:

  • 切断を検出し、QuantumRunner を破棄する
  • ルームに再接続し再参加する
  • SessionRunner.StartAsync() を呼び出して Quantum を再起動する

QuantumSession を停止して破棄するには、次のようにします:

C#

QuantumRunner.ShutdownAll(true);

Unity のメインスレッドにいるときにのみ immediate:true を設定してこのメソッドを呼び出し、絶対に Quantum コールバック内から呼び出さないでください。immediate:false で呼び出すか、Unity 更新コールから取得されるまで手動で遅延させるべきです。

デモメニューサンプルは、新しいゲームを開始したり、実行中のゲームに遅れて参加する方法を示しています。QuantumMenuUIParty.ConnectAsync() では、ゲームがすでに開始されていることを ConnectResult を評価して検出します。

EntityViews と UI

遅延参加や再接続のプレイヤーは、ゲームの構造がどれだけ柔軟であるかに高い要求を突きつけます。ゲームは任意の時点から開始できる必要があり、インスタンス化されたプレハブや UI を再利用しつつ、ゲームをいつでも停止しクリーンアップできるようにする必要があります。副作用として、読み込み時間が長くなったり、新しいシーンで不必要な VFX やアニメーションが表示されたり、UI トランジションに詰まったりすることがあります。

QuantumEntityViewUpdaterQuantumEntityViews を生かして再利用したい場合は、それらが更新されないように手動で停止し、新しい QuantumGame インスタンスに再マッチし、新しいコールバックにサブスクライブする必要があります。

他方、Quantum の取り扱いは非常にシンプルです:ランナーをシャットダウンし、ランナーを開始します。

イベント

クライアントは、プレイヤーが参加または再参加する前に発生した以前のイベントを受け取ることはありません。ゲームビューは、シミュレーションの現在の状態をポーリングして自動的に初期化またはリセットし、将来のイベントやポーリングを使用して自分自身を更新できる必要があります。

SetPlayerData

再接続プレイヤーに対して QuantumGame.AddPlayer(RuntimePlayer data) を呼び出すことは任意です。これは、シミュレーション内のアバター設定ロジックがこれを必要とするかどうかに依存します。

StartParameters.QuitBehaviour

Quantum のシャットダウンシーケンスが実行されているとき(QuantumRunner.ShutdownAll)、QuantumNetworkCommunicator クラスはルーム退室操作を実行するか、LoadBalancing クライアントを切断します。これを自分で処理するためには、QuantumRunner.StartParametersQuitBehaviourQuitBehaviour.None に設定します。

遅延参加とバディスナップショット

Quantum のゲームスナップショットは、検証された(すべての入力が受信された)ティックの後にゲームの完全な状態を含むプラットフォームに依存しないデータの塊です。Quantum シミュレーションはスナップショットから開始され、その状態からシームレスに継続することができます。

クライアントは、シミュレーションがまだ実行中のときに自身のスナップショットを作成できます(ローカルスナップショット)。スナップショットは他のクライアントからリクエストすることもでき(バディスナップショット)、カスタムサーバープラグインが実行中のシミュレーションから送信されることもあります。

スナップショットからの開始や再起動は非常に便利で、Quantum が自動で提供しています。そうしなければ、遅延参加や再接続クライアントはゲームセッションの最初から開始し、サーバーから送信された入力履歴を早送りしなければなりません。これにより、クライアントアプリが無用になり、追いつくまで待機しなければならなくなることがあります。また、サーバーに保存されている入力履歴の時間は約10分に制限されています。

バディスナップショットプロセスは、クライアントが QuantumRunner を開始すると自動的に開始されます(クライアントがセッションを初めて開始する場合でも、遅延参加する場合でも再接続する場合でも)。セッションは一時停止モード DeterministicSession.IsPaused に置かれ、スナップショットがリクエストされます。成功した遅延参加は次のメッセージをログに記録します:

Waiting for snapshot. Clock paused.
Detected Resync. Verified tick: 6541

バディスナップショットは、初回開始の5秒後に接続するクライアントにリクエストされます。

サーバーは、個々のクライアントに負担をかけないように、どのクライアントにバディスナップショットをリクエストするかを決定するための負荷分散メカニズムを使用します。

スナップショットプロセス中のエラーは、Disconnect メッセージを使用してクライアントに送信されます(たとえば、スナップショット待機状態は 15 秒後にタイムアウトします):

名前 説明
Error #13: Snapshot request failed 遅延参加または再参加するクライアントがスナップショットを要求したとき、ルーム/ゲーム内にバディスナップショットを送信できる他のクライアントが存在しません。
Error #51: Snapshot download timeout サーバーがデフォルトのタイムアウトである 20 秒内に必要なすべてのスナップショットフラグメントを送信できませんでした。
Error #52: Snapshot upload timeout 要求されたバディスナップショットがデフォルトのタイムアウトである 10 秒以内にアップロードされませんでした。
Error #53: Snapshot upload error アップロードされたバディスナップショットにエラーが含まれていました。
Error #54: Snapshot upload disconnected 遅延参加がバディスナップショットをアップロード中にクライアントが切断されたため中断されました。

ゲーム開始ルーチン中にスナップショットから起動する際の いくつかの違い があります:

  • CallbackGameStarted の代わりに CallbackGameResynced コールバックが実行されます。
  • スナップショットが受信される前に System.OnInit() が呼び出されます。

ローカルスナップショット

オプションの再接続戦略として、最後の確認されたティックのローカルスナップショットを保存し、新しい QuantumRunner を起動する際に使用できます。これは、オフライン時間が短いと予想される場合に最適です。ローカルスナップショットは一般的に帯域幅に優しく、迅速です。

ガイドライン

Quantum は、ローカルスナップショットの受け入れタイミングについて厳格な制限を課します。古すぎるスナップショットから開始することは、ユーザー体験を悪化させる可能性があるためです。

デフォルトでは、サーバーは 10 秒 より古いローカルスナップショットを受け入れず、代わりにバディスナップショットのリクエストが行われます。このプロセスは透明に機能し、クライアントの視点からは、受信したスナップショットの年齢のみが異なります。

プレイヤー数が少ないゲーム(例:1 対 1)の場合、バディスナップショットを提供できる他のクライアントがオンラインでない可能性が高いため、これらのタイプのゲームは通常 EmptyRoomTTL 値で動作する必要があります。そして Quantum はローカルスナップショット受け入れ時間を EmptyRoomTTL に延長しますが、最大で 2 分 までです。

ワークフロー

  • 切断を検出
  • スナップショットを取得
  • QuantumRunner を終了
  • 高速 Photon 再接続
  • スナップショットで Quantum を再起動
### SetPlayerData

再接続プレイヤーに対して QuantumGame.AddPlayer(RuntimePlayer data) を呼び出すことは任意です。これは、シミュレーション内のアバター設定ロジックがこれを必要とするかどうかに依存します。

StartParameters.QuitBehaviour

Quantum のシャットダウンシーケンスが実行されているとき(QuantumRunner.ShutdownAll)、QuantumNetworkCommunicator クラスはルーム退室操作を実行するか、LoadBalancing クライアントを切断します。これを自分で処理するためには、QuantumRunner.StartParametersQuitBehaviourQuitBehaviour.None に設定します。

遅延参加とバディスナップショット

Quantum のゲームスナップショットは、検証された(すべての入力が受信された)ティックの後にゲームの完全な状態を含むプラットフォームに依存しないデータの塊です。Quantum シミュレーションはスナップショットから開始され、その状態からシームレスに継続することができます。

クライアントは、シミュレーションがまだ実行中のときに自身のスナップショットを作成できます(ローカルスナップショット)。スナップショットは他のクライアントからリクエストすることもでき(バディスナップショット)、カスタムサーバープラグインが実行中のシミュレーションから送信されることもあります。

スナップショットからの開始や再起動は非常に便利で、Quantum が自動で提供しています。そうしなければ、遅延参加や再接続クライアントはゲームセッションの最初から開始し、サーバーから送信された入力履歴を早送りしなければなりません。これにより、クライアントアプリが無用になり、追いつくまで待機しなければならなくなることがあります。また、サーバーに保存されている入力履歴の時間は約10分に制限されています。

バディスナップショットプロセスは、クライアントが QuantumRunner を開始すると自動的に開始されます(クライアントがセッションを初めて開始する場合でも、遅延参加する場合でも再接続する場合でも)。セッションは一時停止モード DeterministicSession.IsPaused に置かれ、スナップショットがリクエストされます。成功した遅延参加は次のメッセージをログに記録します:

Waiting for snapshot. Clock paused.
Detected Resync. Verified tick: 6541

バディスナップショットは、初回開始の5秒後に接続するクライアントにリクエストされます。

サーバーは、個々のクライアントに負担をかけないように、どのクライアントにバディスナップショットをリクエストするかを決定するための負荷分散メカニズムを使用します。

スナップショットプロセス中のエラーは、Disconnect メッセージを使用してクライアントに送信されます(たとえば、スナップショット待機状態は 15 秒後にタイムアウトします):

名前 説明
Error #13: Snapshot request failed 遅延参加または再参加するクライアントがスナップショットを要求したとき、ルーム/ゲーム内にバディスナップショットを送信できる他のクライアントが存在しません。
Error #51: Snapshot download timeout サーバーがデフォルトのタイムアウトである 20 秒内に必要なすべてのスナップショットフラグメントを送信できませんでした。
Error #52: Snapshot upload timeout 要求されたバディスナップショットがデフォルトのタイムアウトである 10 秒以内にアップロードされませんでした。
Error #53: Snapshot upload error アップロードされたバディスナップショットにエラーが含まれていました。
Error #54: Snapshot upload disconnected 遅延参加がバディスナップショットをアップロード中にクライアントが切断されたため中断されました。

ゲーム開始ルーチン中にスナップショットから起動する際の いくつかの違い があります:

  • CallbackGameStarted の代わりに CallbackGameResynced コールバックが実行されます。
  • スナップショットが受信される前に System.OnInit() が呼び出されます。

ローカルスナップショット

オプションの再接続戦略として、最後の確認されたティックのローカルスナップショットを保存し、新しい QuantumRunner を起動する際に使用できます。これは、オフライン時間が短いと予想される場合に最適です。ローカルスナップショットは一般的に帯域幅に優しく、迅速です。

ガイドライン

Quantum は、ローカルスナップショットの受け入れタイミングについて厳格な制限を課します。古すぎるスナップショットから開始することは、ユーザー体験を悪化させる可能性があるためです。

デフォルトでは、サーバーは 10 秒 より古いローカルスナップショットを受け入れず、代わりにバディスナップショットのリクエストが行われます。このプロセスは透明に機能し、クライアントの視点からは、受信したスナップショットの年齢のみが異なります。

プレイヤー数が少ないゲーム(例:1 対 1)の場合、バディスナップショットを提供できる他のクライアントがオンラインでない可能性が高いため、これらのタイプのゲームは通常 EmptyRoomTTL 値で動作する必要があります。そして Quantum はローカルスナップショット受け入れ時間を EmptyRoomTTL に延長しますが、最大で 2 分 までです。

ワークフロー

  • 切断を検出
  • スナップショットを取得
  • QuantumRunner を終了
  • 高速 Photon 再接続
  • スナップショットで Quantum を再起動
Back to top