カットの下-記事の最初の部分の翻訳すべてのプログラマーがゲームネットワーキング 、形成の歴史、およびマルチプレイヤーオンラインゲームのデバイスの原則について知っておくべきこと 。 グレンフィードラー投稿。
はじめに
あなたはプログラマーです。 マルチプレイヤーゲームの仕組みについて考えたことはありますか?
外では、魔法のように見えます。ネットワーク上の2人以上のプレイヤーが、まるで1つの共通の仮想現実に存在するかのように、互いに一貫した同じイベントに参加します。 ただし、プログラマーの観点からは、実際に起きていることの本質は、実際には一見すると思われるものではないことは明らかです。 これは幻想であることがわかります。 大規模な詐欺、手の軽さ。 私たちが共通の現実として認識しているのは、実際には、個々のプレイヤー、空間における彼の位置、観測点に固有の近似にすぎません。
次に、さまざまなジャンルのネットワークゲームのプログラマーが使用するいくつかのトリックと、開発の歴史について説明します。
ハードピアツーピア同期
登場の夜明けに、マルチプレイヤーゲームはピアツーピアネットワークの原則に基づいて動作し、各コンピューターはスタートポロジのネットワークを介して他のコンピューターとデータを交換しました。 現在、このモデルはリアルタイム戦略(RTS)にも見られます。興味深いことに、おそらくマルチプレイヤーゲームが機能する最初の方法であったため、多くの人がこのようなゲームをすべて使用していると考えています。
このアプローチのアイデアは、ゲームを一連のステップ(反復)に分割し、各ステップで、特定の制御コマンドのセットを使用して、さらにゲームの状態を決定することです。 たとえば、「ユニットを移動」、「ユニットを攻撃」、「建物を建設」。 この場合に行う必要があるのは、すべてのゲーム機で同じコマンドセットを実行することです。
もちろん、そのような説明はあまりにも単純に聞こえ、多くの微妙な点を明らかにしていませんが、現代のRTSの仕事のアイデアを明確に示しています。 詳細については、28.8の1500人の射手:Age of Empires and Beyondのネットワークプログラミングを参照してください 。
ソリューションはシンプルでエレガントに見えますが、いくつかの制限があります。
まず、ゲームの完全な決定性を保証することは非常に困難です。 そのため、各動きはすべてのマシンで等しく行われます。 たとえば、異なるマシンの同じユニットはわずかに異なるルートに沿って移動できますが、一方のユニットでは事前に目的地に到着して「状況を保存」し、もう一方のマシンでは絶望的に遅れることがあります。 蝶のように、翼の羽ばたきが世界の反対側にハリケーンを引き起こしているため、時間の経過に伴うわずかな偏差が完全な非同期化につながる可能性があります。
次の制限は、次の動きがすべてのマシンで同じように打たれることを保証するために、そのシミュレーションの前にすべてのゲーム機から制御コマンドが受信されるまで待つ必要があるということです。 これは、各プレイヤーが最も遅い接続/マシンを持つプレイヤーの遅延に等しい遅延でプレイすることを意味します。 RTSゲームは通常、特別なサウンドを再生するか、一部のコスメティックアニメーションを再生することにより、このような遅延をマスクします。 ただし、実際に必要な制御コマンドは、遅延間隔が経過した後でも受信できます。
3番目の制限の理由は、ゲームがゲームの状態を変更する制御メッセージのみをネットワーク経由で送信するという事実です。 これがすべて機能するためには、ゲームの初期状態はすべてのプレイヤーで同じでなければなりません。 これは、ゲーム開始前のプレーヤーが接続し、特別な準備モード(
これらすべての制限にもかかわらず、この作品のモデルはRTSによく適しており、 Command And Conqurer 、 Age Of Empires 、 Starcraftなどのゲームで使用されています。 その理由は、これらのゲームのゲームの状態には通常、多数のオブジェクト(ユニット)に関する情報が含まれているためです。 代わりに、この状態の進化に影響する制御コマンドが送信されます。
一方、他のゲームのジャンルでは、進歩は止まりません。 Doom 、 Quake、およびUnrealから始まるアクションゲームの進化を検討してください。
クライアント/サーバー
ダイナミックアクションゲームの時代には、以前のアプローチの限界がDoomゲームで明らかになりました。Doomゲームは、ローカルネットワークでのみ、そしてインターネットで非常にうまく機能しました。
モデムを使用してインターネット経由で実行中のDoomと2台のマシンを接続することは可能ですが、ゲームは完全にプレイできない(たとえば14.4Kbps PPP接続)からほとんどプレイできない(たとえばSLIPで圧縮して28.8Kbpsに接続する場合)まで低速になります。 これらのタイプの化合物には最小限の実用性があるため、このドキュメントでは直接化合物 ( faqs.org ) のみを考慮します。
疑いもなく、問題はDoomがローカルネットワークでのみ動作するように設計されており、上記のアプローチを使用してマシン間の通信を編成したことでした。 各ステップで、プレーヤーの入力(キーストロークなど)が自分のマシンからネットワークの他のゲームノードに転送されました。各マシンで次のステップをシミュレートする前に、他のすべてのプレーヤーからの入力イベントを受け取る必要がありました。 言い換えれば、移動または射撃する前に、遅れているプレイヤーからの入力イベントが到着するまで待たなければなりませんでした。 上記の記事を書いた人がどのように歯を磨くのかを想像してみてください。
LANを超えて、大学や大企業の適切に機能するローカルネットワークに限定されることなく、ネットワーク通信のモデルを変更する必要がありました。 これはまさに1996年にJohn Carmackが行ったもので、ピアツーピアアプローチの代わりにクライアント/サーバーモデルを使用してQuakeを作成しました。
これで、各マシンのゲームは同じゲームコードを実行しなくなり、他のマシンと通信しなくなりました。 代わりに、プレーヤーはクライアントの役割を引き受け、1台のコンピューター( サーバー)とのみデータを交換しました。 ゲームは、すべてのマシンで確定的である必要がなくなりました。 実際、サーバー上にのみ存在していました。 各クライアントマシンは一種の端末に変わり、サーバーで実行されているゲームのおおよそのバージョンを表示しました。
純粋なクライアント/サーバーモデルでは、クライアントはゲームロジックを担当するコードを実行せず、イベントをキーストローク、マウスの動きなどのみサーバーに送信します。 これに応じて、サーバーはこのプレーヤーの状態を更新し、この状態に関する情報とそれを取り巻くプレーヤーの状態を含むパケットを返します。 クライアントは、滑らかな動きの錯覚を作成するために、受信した状態データ間を補間するだけです。 出力では、クライアント/サーバーモデルを最も純粋な形式で取得します。
これは大きな前進でした。 現在、ゲームの品質はクライアントとサーバー間の接続速度に依存しており、遅れているプレイヤー自身の接続速度には依存していません。 また、ゲームの途中で新しいプレイヤーを接続することが可能になり、プレイヤーの総数が増加しました。 プレーヤーごとのチャンネルの平均負荷が減少しました。
しかし、ここには問題がありました:
DoomからQuakeで終わるネットワークモデルで使用されるすべての決定を覚えて正当化することができますが、本質は常に同じでした。 。 最初は、200ms未満の接続遅延が予想されていました。 優れたプロバイダーを介したデジタル接続を持つ人々は、ゲームで問題を経験しませんでした。 残念ながら、ユーザーの99%はモデムを介して接続されており、原則として、価値のない過負荷のプロバイダーを介して接続されています。 このため、遅延は少なくとも300ミリ秒に等しくなります。 お客様 ユーザーモデム。 サーバー プロバイダーのモデム。 ユーザーモデム。 お客様 これはひどい。
おそらく無礼にそれを置く。 自宅にT1があるので、PPPは初めてでした。 今、私はそのことを心に留めておきます。
もちろん、データ伝送の遅延が問題になりました。
ジョンがQuakeWorldをリリースしたときに行ったことは、ゲーム業界を永遠に変えました。
クライアント側の予測
元のQuakeでは、クライアントとサーバー間の接続に常に遅延がありました。 「進む」をクリックすると、パケットがサーバーに到達して戻ってくるまで好きなだけ待機し、移動を開始するだけです。 「火」を押すと、弾丸が飛び立つまで同じように待ちます。
シューティングゲームなどの最新のゲームでは、これは発生しません。 そのようなゲームはデータ転送遅延をどのようにマスクしますか?
この問題は、歴史的に2つのパスで解決されています。 最初、John CarmackはQuakeWorldのクライアント側予測システムを開発しました。このシステムは、後にTim SweeneyによってUnrealゲームで開発および使用されました。 2番目のステップは、ValveのYahn BernierがCounterStrikeのために開発した遅延補償システムです。 プレイヤーからの入力とゲームの応答の間の遅延を隠す方法-2つの部分のうちの最初の部分について説明します。
John Carmackは、今後のQuakeWorldの計画で次のように書いています。
ここで、サーバーからの応答が来る前でも、クライアント自身にプレーヤーの動きの結果を推測させます。 これはアーキテクチャの非常に大きな変更です。 クライアントは、固体オブジェクトを区別し、質量、摩擦、重力などの物理的特性を認識しなければなりません。 純粋な形のエレガントなクライアント/サーバーモデルは過去のものであることを理解するのは悲しいですが、私は理想主義者というよりは実践者です。
ここで、クライアントは以前よりも多くのコードを実行する必要があります。 もはや、プレイヤーの入力をサーバーに送信し、変更の結果を補間するだけの端末ではありません。 これで、入力イベントの到着後すぐに、プレーヤーのローカルな動きを想定できるようになりました 。
「進む」キーを押すと、すぐに移動が行われ、パケットがクライアントからサーバーへ、またはその逆に移動するまで待つ必要はありません。
このアプローチの複雑さは、ユーザー入力と次の反復に割り当てられた時間だけに基づいて、ローカルで動きを予測することにありません。 困難なのは、クライアントとサーバーがプレーヤーの空間内のポイントと彼がすべきことについて意見が一致しない場合、プレーヤーの移動プロセスを正しく調整することです。
今、あなたは驚くかもしれません。 ねえ、どうやって? クライアントがキャラクターの動きを制御する場合、このプロセスで彼をメインキャラクターにしないでください。 クライアントは単純に動きを計算し、必要な情報をサーバーに送信できます。 問題は、すべてのクライアントがサーバーに「これが私の現在の位置だ」と伝えることができれば、クライアントをハッキングすることは難しくありません。
したがって、遅延を回避するために各プレイヤーがローカルで自分の動きを予測できるという事実にもかかわらず、このようなゲームでは、キャラクターを管理する際にサーバーが最高の優先度を持つ必要があります。 Tim Sweeneyが「Unreal Networking Architecture 」に書いているように、「サーバーはボスです。」
ここからが楽しみです。 クライアントとサーバーの計算が一致しない場合、クライアントはサーバーを決定する必要がありますが、遅延のために、クライアントに既に合格した時点で修正を行う必要があります。 たとえば、パケットがクライアントからサーバーに100ミリ秒で行き、同じ時間が戻った場合、キャラクターの位置の修正は、クライアントがすでに動きを予測できた瞬間より200ミリ秒早く発生するはずです。
クライアントが変更を加えずに「そのまま」調整を行った場合、その瞬間以降に計算したすべてをロールバックし、実際に時間を遡らなければなりません。 顧客がさらに予測を続けながら、これを回避する方法は?
解決策は、プレーヤーの以前の状態と入力イベントに関する情報を保存する循環バッファーを使用することです。 クライアントは、サーバーから修正を受け取ると、修正された時点よりも古い情報をすべて破棄し、調整されたばかりから最後に予測されたものまで、プレーヤーのステータスを再カウントします。バッファー内のバッファーで発生した入力イベントに関する情報を使用 実際、クライアントは、キャラクターのアニメーションの最後のnフレームを、ゲームの世界の残りの部分を固定したまま、いつの間にか「巻き戻し、再生」します。
このアプローチにより、プレイヤーはキャラクターを遅滞なく制御でき、さらにシミュレーションは完全に決定論的になります-クライアントとサーバーで同じ結果が得られ、調整を行う必要はほとんどありません。 ティムスウィーニーはこう言います:
...両方のオプションのベスト:いずれにしても、サーバーは管理プロセスのメインのままです。 ほとんどの場合、クライアントでのプレーヤーの動きのシミュレーションはサーバーで何が起こっているかを正確に反映するため、プレーヤーの状態はほとんど調整されません。 ロケットがプレーヤーに衝突したとき、または敵と衝突したときなど、場合によってのみ、サーバーによってその位置が変更されます。
言い換えれば、プレーヤーの位置は、局所的に予測できない外部要因が彼に作用した場合にのみ調整されます。 もちろん、これはプレイヤーがチートを使用しない場合のみです:)