ネットワークのクライアントとサーバー
Transport Layer API の使用

ホスト・マイグレーション

専用サーバーなしのマルチプレイヤー・ネットワークゲームでは、ゲーム中のピアのひとつがゲーム権限の中枢として機能します。このピアのことをホストと呼びます。このピアがサーバーと「ローカルクライアント」を実行するのに対し、その他のピアはそれぞれが「リモートクライアント」を実行します。

ホストが失われると、ゲームを続行することができません。ホストが失われるのは、プレイヤーがゲームを抜けた場合、ホストの処理が停止あるいはクラッシュした場合、ホストのマシンがシャットダウンされた場合、ホストのネットワーク接続が切れた場合などです。

「ホスト・マイグレーション」機能を使用すると、リモートクライアントのひとつを新しいホストにすることができるので、マルチプレイヤーゲームを継続させることが可能になります。

仕組み

ホスト・マイグレーションが有効になっている場合、マルチプレイヤーゲームを実行中に、ピアのアドレスがゲーム中のピアに分配されます。ホストが失われると、ピアのひとつが新しいホストになり、その他のピアが新しいホストに接続されて、ゲームが継続されます。

新しい NetworkMigrationManager コンポーネントは、Unity Networking HLAPI を使用するマルチプレイヤーゲームにドロップすることが可能です。これによって、元々のホストが失われても新しいホストが設定されてゲームが継続可能になります。以下はエディター インスペクターの NetworkMigrationManager のスクリーンショットです。ここに現在のマイグレーションの状態が表示されます。

Network Migration Manager コンポーネント
Network Migration Manager コンポーネント

NetworkMigrationManager によって、NetworkManagerHUD に似たシンプルなユーザーインターフェースが提供されます。このユーザーインターフェースはテストやプロトタイピングのためのものです。実際のゲームでのホスト・マイグレーションには、カスタムのユーザーインターフェースが実装され、また多くの場合、カスタムのロジック(ユーザーによる入力なしで新しいホストを自動的に選択するなど)も実装されます。

Network Migration Manager のプロトタイピング用 HUD
Network Migration Manager のプロトタイピング用 HUD

古いホストの接続が切れたりゲームを抜けたりしたためにマイグレーションが行われた場合でも、古いホストは、新しいホストのクライアントとしてゲームに再参加できます。

ホスト・マイグレーションが行われる際、シーン内のすべてのネットワーク オブジェクトの SyncVar と SyncList の状態が維持されます。これは、オブジェクトの、シリアライズされたカスタムデータについても同様です。

ホストが失われると、ゲーム中のすべてのプレイヤーオブジェクトが無効になります。別のクライアントが新しいホストで新しいゲームに再参加すると、そのクライアントに対応するプレイヤーがホスト上で再び有効化され、他のクライアント上でオブジェクトが再生成されます。したがって、どのプレイヤーの状態データも、マイグレーション中に失われることはありません。

(注) ホスト・マイグレーション中に保持されるのは、クライアントによって利用可能なデータのみです。サーバー上にしかないデータが存在する場合、新しくホストになるクライアントはそのデータを利用することができません。したがって、SyncVar や SyncList に保存されていないホスト上のデータは、ホスト・マイグレーションが行われた後には利用不可能になります。

クライアントが新しくホストになると、コールバック関数 OnStartServer がすべてのネットワーク オブジェクトに実行されます。

新しいホスト上で、NetworkMigrationManager が関数 BecomeNewHost を使用して、現在の ClientSceneto の状態を元に、ネットワーク化されたサーバーシーンを構築します。

ホスト・マイグレーションが有効になったピアは、その connectionId によってサーバー上で識別されます。クライアントがゲームの新しいホストに再接続されると、この connectionId が新しいホストに渡され、古いホストに接続されていたクライアントにこのクライアントがマッチされます。この Id は ClientScene で “reconnectId” として設定されます。

非プレイヤー オブジェクト

ホスト・マイグレーションには、クライアント権限を持った非プレイヤー オブジェクトの処理も含まれます。プレイヤー オブジェクト同様、各クライアントが持つオブジェクトが、いったん無効化されたうえで再有効化されます。

ピアの識別

ホストが失われるまでは、すべてのピアはホストに接続されています。それぞれのピアが固有の connectionId をホスト上に持っています。この Id はホスト・マイグレーションにおいては “oldConnectionId” と呼ばれます。

新しいホストが選択されてピアがそのホストに再接続されると、各ピアによって “oldConnectionId” が提供されます。これはそれぞれのピアを識別するためのものです。これによって新しいホストは、再接続したクライアントを、対応するプレイヤーオブジェクトとマッチさせることができるようになります。

古いホストの使用は 0 の特別な oldConnectionId に再接続します - 古いホストへの接続を持っていなかったので、ホストは古いホストです。このために ClientScene.ReconnectIdHost があります。

ビルトインのユーザーインターフェースを使用する場合、oldConnectionId は自動で設定されます。これは NetworkMigrationManager.Reset あるいは ClientScene.SetReconnectId を使用して手動で設定することも可能です。

ホスト・マイグレーションの流れ

  1. マシン A が、ホスト・マイグレーションが有効になったゲームをホストする

  2. マシン B がクライアントを開始し、ホスト・マイグレーションが有効になったゲームに参加する
    • マシン B がピアの情報を受け取る(MachineA–0、 self (MachineB)–1)
  3. マシン C がクライアントを開始し、ホスト・マイグレーションが有効になったゲームに参加する
    • マシン B がピアの情報を受け取る (MachineA–0、 MachineB–1、 self (MachineC)–2)
  4. マシン A が失われる(したがってホストが失われる)

  5. マシン B のホストへの接続が切断される
    1. マシン B のコールバック関数がクライアント上で MigrationManager に実行される
    2. マシン B 上ですべてのプレイヤーのプレイヤーオブジェクトが無効になる
    3. マシン B はオンラインシーンに留まる
  6. マシン B がユーティリティー関数を使用して MachineB 自体を新しいホストとして選択する
    1. マシン B が BecomeNewHost() を呼び出す
    2. マシン B がリスニングを開始する
    3. マシン B の自身のプレイヤーオブジェクトが再アクティベートされる
    4. マシン B のプレイヤーが以前の状態を保持してゲームに戻りました。
  7. マシン C のホストへの接続が切断される
    1. マシン C のコールバック関数が、クライアント上で MigrationManager に実行される
    2. マシン C のプレイヤーオブジェクトが、すべてのプレイヤーに対して無効化される
    3. マシン C はオンラインシーンに残る
  8. マシン C がユーティリティー関数を使用して マシン B を新しいホストとして選択する
    • マシン C が新しいホストに再接続される
  9. マシン B が マシン C からの接続を受け入れる
    1. マシン C が、(AddPlayer メッセージではなく、) oldConnectionId で再接続メッセージを送信する
    2. サーバー上で MigrationManager にコールバック関数が実行される
    3. マシン B が、oldConnectionId を使用して、そのプレイヤーの無効になったプレイヤーオブジェクトを見付け、ReconnectPlayerForConnection() によってそれを再び追加する
    4. プレイヤー オブジェクトが マシン C 上で再生成される
    5. マシン C のプレイヤーが以前の状態を保持ししてゲームに戻りました。
  10. マシン A がゲームに戻る(古いホスト)
    1. マシン A がユーティリティー関数を使用して、新しいホストに マシン B を選択する
    2. マシン A が マシン B に「再接続」される
  11. マシン B が マシン A からの接続を受け入れる

  12. マシン A は 0 の oldConnectionId と再接続メッセージを送信します。
    1. サーバー(マシンB)上で MigrationManager にコールバック関数が実行される
    2. マシン B が oldConnectionId を使用して、そのプレイヤーの無効なプレイヤーオブジェクトを見付け、それをReconnectPlayerForConnection() で再び追加する
    3. マシン A でプレイヤーオブジェクトが再生成される
    4. マシン A のプレイヤーが以前の状態を保持してゲームに戻りました。

コールバック関数

NetworkHostMigrationManager のコールバック関数

// called on client after the connection to host is lost. controls whether to switch scenes
protected virtual void OnClientDisconnectedFromHost(
    NetworkConnection conn, 
    out SceneChangeOption sceneChange)

// called on host after the host is lost. host MUST change scenes
protected virtual void OnServerHostShutdown()

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId)

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId, 
    NetworkReader extraMessageReader)

// called on new host (server) when a client from the old host re-connects a non-player object
protected virtual void OnServerReconnectObject(
    NetworkConnection newConnection, 
    GameObject oldObject, 
    int oldConnectionId)

// called on both host and client when the set of peers is updated
protected virtual void OnPeersUpdated(
    PeerListMessage peers)

// utility function called by the default UI on client after connection to host was lost, to pick a new host.
public virtual bool FindNewHost(
    out NetworkSystem.PeerInfoMessage newHostInfo, 
    out bool youAreNewHost)

// called when the authority of a non-player object changes
protected virtual void OnAuthorityUpdated(
    GameObject go,
    int connectionId,
    bool authorityState)

制約

サーバー(ホスト)のみに存在していたデータは、ホストの接続が切れると失われます。ホスト・マイグレーションが正常に実行されるためには、重要なデータを(サーバー上だけに保管するのではなく)クライアントに配信する必要があります。

上記は、ゲームが直接接続を使用している場合に当てはまる説明です。マッチメーカーやリレーサーバーを使用している場合には、追加の作業が必要となります。

ネットワークのクライアントとサーバー
Transport Layer API の使用