Quake Engine code review : Network (2/4)の和訳をしてみる

#Quake Engine code review : Network (2/4)
QuakeWorldのネットワークアーキテクチャは当時、画期的な画期的な革新と見なされていました。 すべてのネットワークゲームの後継者は同じアプローチを採用しました。 ここに詳細があります。

この記事は４つのセクションに分かれています

1. [Architecture section](http://qiita.com/UnagiHuman/items/cf30a55bbb6b604fdb94)
2. [Network section](http://qiita.com/UnagiHuman/items/2806ac5f2e51c6a5b3be)（本記事）
3. [Prediction section](http://qiita.com/UnagiHuman/items/5ef6d1eb7c8672779561)
4. Rendition section

#Network stack 
Quakeの基本的なコミュニケーションユニットがコマンドです。 コマンドはプレーヤーの位置、方向、健康、損傷などを更新するために使用されます。
TCP / IPには、リアルタイムシミュレーション（フロー制御、信頼性、パケットシーケンシング）には都合が良いが、Quake World Engine（元のQuakeにあった）には使えません。FPSでは、ASAP（as soon as possible）で受信されない情報は再送信する価値がありません。 したがって、UDP / IPが選択され、信頼性とパケットシーケンスを実装するために、ネットワーク抽象化レイヤ "NetChannel"が作成されました。
OSIの観点から、NetChannelはUDPの上に非常にうまく座っています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/116884/474f3711-9ce1-dd32-aba3-eaac1ab4ae70.png)

要約すると、エンジンは主にコマンドを扱います。 送信または受信が必要な場合は、タスクをnetchan.cのNetchan_TransmitメソッドとNetchan_Processメソッドに委譲します（これらのメソッドはクライアントとサーバーで同じです）。

#NetChannel Header 
NetChannelヘッダーの構造は次のとおりです。

| Bit offset       |      Bits 0-15 |    16-31    |
|:-----------------|------------------:|:------------------:|
| 0             |              Sequence      |Sequence  |
| 32             |              ACK Sequence |       ACK Sequence  |
| 64             |              QPort |       Commands  |
| 94             |              ... |       ...  |

シーケンスは、送信者によって初期化され、パケットが送信されるたびに1ずつインクリメントされるintです。 シーケンスの目的は複数ですが、最も重要なのは、受信者に失われた/重複した/順序付けられていないUDPパケットを認識させる方法を提供することです。 intの最強ビットはシーケンスの一部ではありませんが、ペイロード（コマンド）に信頼できるデータが含まれていることを示すフラグが付いています（詳細は後で説明します）。
     ACK Sequenceもintであり、受信した最後のシーケンス番号と等しい。 これにより、NetChannelのもう一方の端で、パケットが失われたかどうかを確認できます。
     QPortはNATルータのバグを回避するためにここにあります（このページの末尾にある続きを読んでください）。 この値は、クライアントの起動時に設定される乱数です。
     コマンド：ペイロードです

#Reliable messages 
信頼できないコマンドは、最後の発信シーケンス番号でマークされて送信されたUDPパケットにグループ化されます。彼らが迷子になった場合、送信者にとっては問題になりません。
信頼性の高いコマンドは別々に扱われますが、重要なのは、送信者と受信者の間で肯定応答されない信頼できるUDPパケットが1つしかないことを理解することです。

すべてのゲームループは、新しい信頼できるコマンドが生成された場合、
message_buf配列に追加されます（メッセージ変数を介してパイロットされます）（1）。
信頼できるコマンドのセットは、メッセージからreliable_bufアレイ（2）に移動されます。これは、reliable_bufが空の場合にのみ発生します（空でない場合、これは他のコマンドセットが先に送信され、まだ確認応答されていないことを意味します）。

最終的なUDPデータグラムが作成されます。NetChannelヘッダーが追加されます（3）。十分なスペースがある場合は、reliable_bufのコンテンツと信頼性の低いコマンドが追加されます。

受信側では、UDPメッセージが解析され、入ってくるシーケンス番号は（パケットが信頼できるデータを含むことを示すビットフラグと共に）発信シーケンスACK（4）に転送される。

次のメッセージを受信したとき：
信頼できるビットフラグがtrueに設定されている場合、UDPパケットはそれを受信者に送信します。 NetChannelはreliable_buf（5）をクリーンアップして、新しいコマンドセットを送信する準備ができています。
信頼できるビットフラグがfalseに設定されている場合、UDPはそれを受信者に送信しません。 NetChannelはreliable_bufの内容を再度送信しようとします。 新しいコマンドがmessage_bufに積み重なります。この配列がオーバーフローした場合、クライアントは破棄されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/116884/cdc3ad1f-3356-d2d4-2307-63efcb924d5d.png)

##Flow-Control 
私が読むことができる限り、サーバ側でのみフロー制御があります。 クライアントは状態更新をできるだけ速く送信します。
サーバ上でアクティブな最初のフロー制御ルールは、データグラムがクライアントから受信された場合にのみデータグラムを送信することです。 制御フローの第2の形態は、クライアントがコンソール内のレートコマンドを介して設定できるパラメータである「choke」である。 これにより、サーバーは更新メッセージをスキップし、クライアントに送信されるデータの量を減らします。

##Important commands 
コマンドにはバイトに格納されたタイプコードと、それに続くコマンドのペイロードがあります。 おそらく最も重要なのは、ゲームの状態（frame_t）に関する情報を与えるコマンドです。

1. svc_packetentitiesとsvc_deltapackententities：ロケットトレイル、爆発、パーティクルなどのエンティティを更新する
2. svc_playerinfo：プレーヤーの位置、最後のコマンドとコマンドの継続時間をmsec単位で更新する

##More on the qport 
Qportはバグを回避する為にNetChannelヘッダーに追加されたものです。 Qport導入以前は、Quakeサーバーはその組み合わせ（リモートIP、リモートUDPポート）によってクライアントを識別しました。 ほとんどの場合、これはうまくいきましたが、特定のNATルータは、ポート変換（リモートUDPポート）のスキーマを散発的に変更できます。 UDPポートが信頼できない場合、John Carmack氏は彼の計画の1つで、（リモートIP、NetChannelヘッダーのQport）でクライアントを識別することに決めたと説明しました。 これにより、混乱が修正され、サーバはターゲットUDP応答ポートを即座に調整することができました。

##Latency calculation 
Quakeエンジンは、最後に送信された64個のコマンド（frame_t配列：フレーム内）を送信時刻と共に格納します。これらのコマンドは、転送に使用されたシーケンス番号（outgoing_sequence）を介して直接アクセスできます。

```csharp
frame = &cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK];
frame->senttime = realtime;
				
	//Send packet to server
```

サーバからの肯定応答に応じて、コマンドが送信された時刻がシーケンスACKを介して取得されます。 レイテンシは次のように計算されます。

```csharp
//Receive response from server 
				
frame = &cl.frames[cls.netchan.incoming_acknowledged & UPDATE_MASK];
frame->receivedtime = realtime;
latency = frame->receivedtime - frame->senttime;
```

##Some elegant things 
配列インデックスサイクリング

エンジンのネットワーク部分には、最後に受信した64個のUDPデータグラムが格納されます。 配列を循環する素朴なアプローチは、モジュロ演算子を使うことでした。

arrayIndex =（oldArrayIndex + 1）％64;

代わりに、新しい値は、UPDATE_MASKで "AND"バイナリ演算で計算され、UPDATE_MASKは64-1に等しくなります。
(2のべき乗の剰余を処理する場合、ビット単位AND演算を使用することができる。)

arrayIndex =（oldArrayIndex + 1）＆UPDATE_MASK;

実際のコードは実際には：

```
frame_t * newpacket; newpacket =＆frames [cls.netchan.incoming_sequence＆UPDATE_MASK];
```