9. ステーション不一致の検出

9.1. 概要

サーバークライアントネットワーク型以外のネットワークトポロジーにおいて、パケットロス率が高いと、一部ステーション間のセッションが 切れてしまう場合があります。

一部ステーション間のセッションが切れても、ほかのステーション間は通信が行われているため、 ある特定のステーション間で通信ができない状態でゲームが進行してしまいます。 また、 ホストとホスト候補(二番目にジョインしたステーション)の間の通信が切れると、 ホスト候補がホストに昇格してしまい、ひとつのセッションにホストが2つできてしまいます( Figure 9.1 参照)。 もし、ホストが2つできた場合は、ホストになったステーションが独立に他のステーションへ指示を出すため、セッション全体の動作がおかしくなることが考えられます。

../_images/Fig_Station_Inconsistency.png

Figure 9.1 ホスト間の通信切断によりホストが2つできる問題の説明図

NEXにおいては、これら問題に対して、自動で検出を行い、ステーションを 切断することで復帰可能であれば切断を試みます。 復帰できない状態では、フェイタルエラーを発生させて、 すべてのステーションを切断して、終了可能な状態にします。 また、ホスト候補のホスト昇格時には、ホストの切断を他のステーションも認識しているか、 投票を行い、過半数の切断確認の投票があったらホスト候補がホストに昇格することにより、 ホストとホスト候補の間の通信が切れたことによる二つのホスト同時存在の確率を低くします。

アプリケーションは、GetFatalErrorでのフェイタルエラーを監視と、 フェイタルエラー発生時のNetZ::Terminate()の実行および、NetZのオブジェクトの削除が必要です。

ホストマイグレーション拡張機能利用時には、NotificationEventHandlerにより通知される NotificationEvents::HostChangeEventをハンドリングして、Session::GameOver()を実行することを推奨します (9.3. 節参照)。

9.2. ステーション不一致検出動作

NEXにおいては、ホストが認識しているステーションリストを子へ定期的に送信して、 子は、ホストからのそのステーションリストと比較して以下の2点の処理を行います。

まず、一定期間ホストからの通知と比較して、子はホストに存在するステーションが存在しないままの場合 フェイタルエラー(QERROR(DOCore, StationInconsistency)/GetFatalError()を参照) を発生させて、NetZ::Terminateの実行およびNetZのオブジェクトの削除を要求します。 また、ホストが認識していないステーションとの接続を自動切断することにより、 二つのホストを頂点とした二つの異なるネットワークへ分断します。

ホスト候補がホストのセッション切れを認識していない状態で、一部子がホストとのセッション切れを 認識する場合もあります。 子がホスト候補との通信により、ホスト候補がホストへ昇格する意思がないことを 確認し、フェイタルエラー(QERROR(DOCore, FaultRecoveryJobProcessFailed)/GetFatalError()を参照)を発生させて、 NetZ::Terminateの実行およびNetZのオブジェクトの削除を要求します。

ホスト候補とホストのセッション切れを認識した場合には、ホストの切断を他のステーションも認識しているか 投票を行います。ステーションは、切断を認識していたら、即座に切断を返します。接続中であったら、 StreamSettings::GetMaxSilenceTime()の時間の間、切断しないかチェックを行い、切断したら切断を返し、 タイムアウト後も接続していたら、接続と返します。 ホスト候補は、回答があった投票結果のうち、自分を含めた切断という投票が過半数に達したら、ホストへ昇格します。 過半数に達しなかった場合は、フェイタルエラー(QERROR(DOCore, FaultRecoveryJobProcessFailed)/GetFatalError()を参照) を発生させて、NetZ::Terminateの実行およびNetZのオブジェクトの削除を要求します。

9.3. ホストマイグレーション拡張機能利用時の留意事項

二つの異なるネットワークに分断され、ホストマイグレーション拡張機能(HostMigrationExtension)を 利用している場合には、後からホストに昇格したホストがゲームサーバーに登録されます。 この際に、前のホストに NotificationEventHandler により、 NotificationEvents::HostChangeEvent が通知されます。

NotificationEvents::HostChangeEvent が発生した側のホストには、新規ジョインが発生することがありません。

また、二つのネットワークは同じGatheringIDを持つため、このままゲームを続行すると、 ホストが昇格するごとに、異なるネットワークのホストが登録される可能性があります。

このような不具合が発生いたしますので、 NotificationEvents::HostChangeEvent が通知されたホストは、Session::GameOver()を実行し、 自分の支配下にある側のネットワークを解散する処理を行うことを推奨します。

9.4. パラメータチューニング

あるステーションがホストにジョインした後、最後の子にジョインするまで、NAT トラバーサルとジョイン処理の分遅延します。 よって、親からの通知に比べてステーションが足りないことを認識するまでの時間は、NAT トラバーサルとジョイン処理にかかる時間以上の 値にする必要があります。

具体的には、ホストが認識しているステーションリストを子へ定期送信する間隔(Session::SetSyncStationListInterval() )と、 子がステーションが足りないと判断するチェック回数の閾値(Session::SetDetectStationInconsitencyThresholdCount() )を 掛け合わせた値がRootTransport::GetNATTraversalTimeout()の値より5000から10000[msec]大きくなるように指定してください。

RootTransport::SetNATTraversalTimeout()は、ゲームに参加できる最大人数×[3000-5000]を目安に設定するので、 Session::SetSyncStationListInterval()を上記の[3000-5000]で採用した値として、 Session::SetDetectStationInconsitencyThresholdCount()を最大人数+1~3としてください。

9.5. 検証方法

ステーション不一致状態を発生させるには、ネットワークエミュレータにより高い確率でパケットロスが発生する環境でエージングするか、 任意のステーションを落とす方法があります。

NEXには、デバッグ用にEmulationDevice クラスによるネットワークエミュレータ機能が実装されており、以下のような設定で、ネットワークエミュレータで ロスを設定可能です(18.1.1. 節参照)。ロスの確率は、15%以上の値を推奨します。

Code 9.1 ロス率の設定
nn::nex::OutputEmulationDevice * pOutputEmulation =
                nn::nex::RootTransport::GetInstance()->GetOutputEmulationDevice();
pOutputEmulation->Enable(); //Enableの後に、値を設定すること
pOutputEmulation->SetPacketDropProbability(0.15);

さらに無応答時に送信するキープアライブパケットのタイムアウト値を StreamSettings::SetKeepAliveTimeout()で長くすることで、 障害検出までに送られるキープアライブパケット数を減らし切れやすくすることができます。

Code 9.2 キープアライブパケットのタイムアウト値の設定
nn::nex::Stream::GetSettings()::SetKeepAliveTimeout(5000);
//切れやすくなる設定。デフォルトは、1000

また、デバッグ用の機能として任意のステーションを強制切断することによっても検証可能です。 Station::SignalFaultによって、ステーションを強制切断します。

いずれの場合にも、ステーション不一致発生時には、EventLog::Warning、もしくは、EventLog::Fatalレベルの ログメッセージ出力されます。