#Quake Engine code review : Prediction (3/4)
前回の記事ではネットワーク通信のためのNetChannel抽象化を見ただけです。 今回の記事では予測がどのようにレイテンシを補うかを見ていきます。
ここにいくつかの追加資料があります:
この記事は4つの部分に分かれています:
Architecture section
Network section
Prediction section (本記事)
Rendition section
#Prediction
おそらく予測はおそらく最も困難であり、最も資料が少なく、
Quake World Engineの最も重要な部分です。
予測の目標は、レイテンシを上回ること、すなわち、媒体が情報を送信するのにかかる遅延を補償することである。
これはクライアント側で行われ、プロセスは「クライアント側予測」と呼ばれ、サーバー側にはラグ補償技術はありません。
したがって、gamestateはlatency / 2です。
コマンドを送信する時間が含まれている場合、私たちは行動の結果を見るために往復(待ち時間)を待たなければなりません:
Quake予測システムを理解するための鍵は、NetChannelが "frames"変数(frame_tの配列)をどのように埋め込むかを理解することです。
サーバーに送信されたすべてのコマンドは、senttimeとともにインデックスnetchannel.outgoingsequenceにフレームとして保存されます。
サーバがsequenceACK経由でコマンドの受信を確認すると、送信されたコマンドを取得してレイテンシを計算できます。
待ち時間= senttime-receivedtime;
この時点で、私たちはlatency / 2前にレイテンシのように世界を持っています。 NATレイテンシは良いです(50ms未満)が、Interetでは(200ms以上)巨大であり、現在世界をシミュレートするために予測が行われなければなりません。 これは、ローカルプレイヤーや他のプレイヤーでは異なる方法で行われます
#Local player
ローカルプレイヤーの場合、待ち時間はサーバー状態となるものを外挿することによって0にかなり低減されます。 これは、サーバーから最後に受信した状態を使用し、以後に送信されたすべてのコマンドを再生することによって行われます。
したがって、クライアントは、t +latency/ 2でのサーバ上の位置を予測します。
コードの観点から見ると、これはCL_PredictMoveメソッドで実行され、最初にQuakeエンジンが再生可能なコマンドの送信制限を決定します。
cl.time = realtime - cls.latency - cl_pushlatency.value * 0.001;
注:cl_pushlatencyは、クライアント側のcvarセットで、クライアントの待ち時間(ミリ秒単位)を引いた値になります。 したがって、我々はかなり結論づけることができます:cl.time = realtime。
その後、他のすべてのプレイヤーはCL_SetSolidPlayers(cl.playernum)を介してsolid状態になります。 (したがって、衝突をテストすることができます)、最後の受信状態から送信されたコマンドは、cl.time <= to-> senttime(CL_PredictUsercmdを介して衝突が各繰り返しでテストされる)まで再生されます。
#Other players
他のプレイヤーにとっては、Quakeエンジンには「コマンド送信済みだがまだ認識されていない」というものがないので、代わりに外挿が使用されます。 最後の既知の位置から開始して、cmdは使用位置を予測するために拡張されています。 角度の回転は予測されず、位置のみが予測されます。
Quake Worldは他のプレイヤーのレイテンシも考慮に入れています。 各クライアントの待ち時間はworldupdateとともに送信されます。
#Code
予測と衝突コードは次のように要約できます。
CL_SetUpPlayerPrediction(false)
CL_PredictMove
| /* Local player is moved */
| CL_SetSolidPlayers
| | CL_PredictUsercmd
| | PlayerMove
| Interpolate linearely
CL_SetUpPlayerPrediction(true)
CL_EmitEntities
CL_LinkPlayers
| /* Other players is moved */
| for every players
| | CL_SetSolidPlayers
| | CL_PredictUsercmd
| | PlayerMove
CL_LinkPacketEntities
CL_LinkProjectiles
CL_UpdateTEnts
この部分は、Quake Workがプレイヤーの予測を実行するだけでなく、予測に対して衝突検出を実行する必要があるため、複雑です。
##CL_SetUpPlayerPrediction(false)
最初の呼び出しは予測を実行せず、サーバから受信した他のプレイヤーのみを設定します(過去にはt-latency / 2になっています)。
##CL_PredictMove()
これがローカルプレイヤーの動きが行われる場所です。
オリエンテーションは補間されず、完全なリアルタイムです。
位置と速度:現在までに送信されたすべてのコマンド(cl.time <= to-> senttime)は、サーバーから受信した最後の位置/速度に適用されます。
位置と速度の更新に関する詳細:
他のプレイヤーは、CL_SetSolidPlayersを介して最初にSolid状態になります(最後にCL_SetUpPlayerPrediction(false)で設定された位置を知っています)。
送信されたコマンドに対するエンジンループ、衝突のチェック、CL_PredictUsercmdによる位置の予測、他のプレイヤーとの衝突もテストされます。
結果の位置と速度はcl.sim *に格納されますが、後でPOVの設定に使用されます。
##CL_SetUpPlayerPrediction(true)
2回目のコールでは、現在のサーバ側で他のプレイヤーの位置が予測されます(ただし、まだ移動は実行されていません)。 位置は最後の既知のコマンドと最後に知っている位置で外挿されます。
注:ここでは少し問題があります:Valveが推奨するの(cl_pushlatency用)は、サーバー側でt +latency/ 2で予測されるローカルプレーヤーを持つことになります。 しかし、他のプレイヤーの位置はサーバ側で予測される。 おそらくQWのcl_pushlatencyの最適な設定は-latency / 2?
#CL_EmitEntities
ここでは、visibiliy edictsが生成されます。 これらはレンダリンに影響を与える
- CL_LinkPlayers:他のプレイヤーの移動が行われ、他のプレイヤーは順番にsolid状態に変わり、予想位置に対して衝突検出が実行されます。
- CL_LinkPacketEntitiesPacket:サーバーから受け取った最後の状態のentitiesが予測され、visibiliy edictsにリンクされます。 これはあなたが発射するミサイルが遅れている理由です。
- CL_LinkProjectiles:Nails and stuff
- CL_UpdateTEnts:標準ライトビームとentitiesが更新されます。