Merge pull request #41 from seasidelab/minimize-doc-diff
Minimize documentation differences.
This commit is contained in:
14
README.ja.md
14
README.ja.md
@@ -1,19 +1,19 @@
|
||||

|
||||
|
||||
## GGPOとは?
|
||||
_[](https://ci.appveyor.com/project/pond3r/ggpo/branch/master)_
|
||||
|
||||
従来の技術はプレイヤーの入力に遅延を織り込んで通信を行っており、その結果反応が遅く、ラグを感じるプレイ感になっていました。ロールバックネットワーキングはプレイヤーの入力を即座に送信するため、入力予測と投機的実行を行うことにより、遅延ゼロの回線環境をもたらせるのです。ロールバックがあれば、タイミングや相手の動きや効果音に対しての反応、指が覚えている入力、これらオフラインで行えた内容が、そのままオンラインでも行えます。GGPOネットワーキングSDKは、ロールバックネットワーキングを新作や発売されているゲームに極力簡単に組み込めるよう作られています。
|
||||
## GGPOとは
|
||||
|
||||
これまでのGGPOについてさらに知りたい方は、http://ggpo.net/をご覧ください。
|
||||
従来の技術はプレイヤーの入力に遅延を織り込んで通信を行っており、その結果反応が遅く、ラグを感じるプレイ感になっていました。ロールバックネットワーキングは入力予測と投機的実行を行って、プレイヤーの入力を即座に送信するため、遅延を感じさせないネット環境をもたらします。ロールバックがあれば、タイミングや相手の動きや効果音に対する反応、指が覚えている入力、これらオフラインで行えた内容が、そのままオンラインでも行えます。GGPOネットワーキングSDKは、ロールバックネットワーキングを新作や発売されているゲームに極力簡単に組み込めるよう作られています。
|
||||
|
||||
これまでのGGPOについてさらに知りたい方は、http://ggpo.net/ をご覧ください。
|
||||
|
||||
このリポジトリにコードやドキュメント、SDKのサンプルアプリケーションが収められています。
|
||||
|
||||
|
||||
## ビルド
|
||||
|
||||
GGPOのビルドは現在のところWindowsのみになりますが、他プラットフォームへのポートも現在行っています。
|
||||
|
||||
|
||||
### Windows
|
||||
|
||||
Windowsのビルドは[Visual Studio 2019](https://visualstudio.microsoft.com/downloads/)と[CMake](https://cmake.org/download/)が必要になります。ご使用の前に、どちらもインストールされていることをご確認ください。またインストール時、パスにCMakeを追加してください。
|
||||
@@ -23,7 +23,6 @@ Windowsのビルドは[Visual Studio 2019](https://visualstudio.microsoft.com/do
|
||||
|
||||
好みにあわせて`cmake-gui`で実行も出来ます。
|
||||
|
||||
|
||||
## サンプルアプリケーション
|
||||
|
||||
ソースディレクトリ内のVector Warアプリケーションでは、2つのクライアントを同期するGGPOが搭載されています。コマンドライン引数は
|
||||
@@ -32,8 +31,7 @@ Windowsのビルドは[Visual Studio 2019](https://visualstudio.microsoft.com/do
|
||||
vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>) for each player
|
||||
```
|
||||
|
||||
2~4プレイヤーゲームの始め方についての例は、binディレクトリにある.cmdファイルをご覧ください。
|
||||
|
||||
2~4プレイヤーゲームの始め方についての例は、binディレクトリにある.cmdファイルをご覧ください。
|
||||
|
||||
## ライセンス
|
||||
|
||||
|
||||
10
README.md
10
README.md
@@ -3,16 +3,19 @@
|
||||
_[](https://ci.appveyor.com/project/pond3r/ggpo/branch/master)_
|
||||
|
||||
## What's GGPO?
|
||||
Traditional techniques account for network transmission time by adding delay to a players input, resulting in a sluggish, laggy game-feel. Rollback networking uses input prediction and speculative execution to send player inputs to the game immediately, providing the illusion of a zero-latency network. Using rollback, the same timings, reactions visual and audio queues, and muscle memory your players build up playing offline translate directly online. The GGPO networking SDK is designed to make incorporating rollback networking into new and existing games as easy as possible.
|
||||
|
||||
Traditional techniques account for network transmission time by adding delay to a players input, resulting in a sluggish, laggy game-feel. Rollback networking uses input prediction and speculative execution to send player inputs to the game immediately, providing the illusion of a zero-latency network. Using rollback, the same timings, reactions, visual and audio queues, and muscle memory your players build up playing offline will translate directly online. The GGPO networking SDK is designed to make incorporating rollback networking into new and existing games as easy as possible.
|
||||
|
||||
For more information about the history of GGPO, check out http://ggpo.net/
|
||||
|
||||
This repository contains the code, documentation, and sample applications for the SDK.
|
||||
|
||||
## Building
|
||||
|
||||
Building GGPO is currently only available on Windows, however efforts are being made to port it to other platforms.
|
||||
|
||||
### Windows
|
||||
### Windows
|
||||
|
||||
Windows builds requires both [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) and [CMake](https://cmake.org/download/). Make sure you've installed both before starting. Make sure to add CMake to your path when installing.
|
||||
|
||||
- Run the `build_windows.cmd` in the root directory of the SDK to generate the Visual Studio 2019 solution files.
|
||||
@@ -21,6 +24,7 @@ Windows builds requires both [Visual Studio 2019](https://visualstudio.microsoft
|
||||
You can also run the `cmake-gui` tool if you prefer.
|
||||
|
||||
## Sample Application
|
||||
|
||||
The Vector War application in the source directory contains a simple application which uses GGPO to synchronize the two clients. The command line arguments are:
|
||||
|
||||
```
|
||||
@@ -30,5 +34,5 @@ vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>)
|
||||
See the .cmd files in the bin directory for examples on how to start 2, 3, and 4 player games.
|
||||
|
||||
## Licensing
|
||||
GGPO is available under The MIT License. This means GGPO is free for commercial and non-commercial use. Attribution is not required, but appreciated.
|
||||
|
||||
GGPO is available under The MIT License. This means GGPO is free for commercial and non-commercial use. Attribution is not required, but appreciated.
|
||||
|
||||
@@ -12,7 +12,6 @@ GGPOネットワークライブラリ・開発者向けガイドは、アプリ
|
||||
|
||||
ゲームエンジンの中にはゲームステートでも入力でもないものが多数あります。例えばオーディオやビデオレンダラーはゲームの結果に影響を及ぼさないので、ゲームステートではありません。ゲームに影響を及ぼさないような、特別効果を生成する特別効果エンジンがあれば、ゲームステートから除外することができます。
|
||||
|
||||
|
||||
## 同期にステートと入力を使用する
|
||||
|
||||
GGPOを使用したゲームで遊ぶ各プレイヤーは、現在遊んでいるゲームの完全なコピーを持っています。両プレイヤーが同じゲーム内容を遊べるよう、GGPOは両プレイヤーにあるゲームステートのコピーを同期し続ける必要があります。プレイヤー間で各フレームごとにゲームステート全コピーを送信することは大きな負担になってしまいます。代わりにGGPOはお互いの入力を送信し、互いのゲームを一つ一つ進めています。この機能のために、ゲームエンジンは3つの条件を満たさねばなりません。
|
||||
@@ -21,20 +20,18 @@ GGPOを使用したゲームで遊ぶ各プレイヤーは、現在遊んでい
|
||||
- ゲームステートが完全にカプセル化(encapsulated)されていること、そして直列化が可能(serializable)であること。
|
||||
- ゲームエンジンがそのフレーム時のゲーム内容をレンダリングすることなく、ロード、セーブ、フレームのシミュレーションができること。これはロールバック実行の際に使用します。
|
||||
|
||||
|
||||
## プログラミングガイド
|
||||
|
||||
以下のセクションはあなたのアプリをGGPOにポーティング、移植する一連の流れを紹介します。GGPO APIの詳細な説明は、以下のGGPO参照セクションをご覧ください。
|
||||
|
||||
|
||||
### GGPOとのインターフェイス
|
||||
|
||||
GGPOは新旧のゲームエンジンと簡単にインターフェイスできるよう設計されています。GPOSessionCallbacksのフックを介してアプリに呼び出すことにより、大分のロールバックの実装を行います。
|
||||
|
||||
|
||||
### GGPOSessionオブジェクトの生成
|
||||
|
||||
`GGPOSession`オブジェクトはGGPOフレームワークへのインターフェイスです。ローカル、IPアドレス、対戦したいプレイヤーをバインドするためのポートを渡す`ggponet_start_session`ファンクションを使用して、作成します。またセッションが1プレイヤー側、2プレイヤー側いずれの場合でも、ゲームステートを管理するコールバック関数で満たされた`GGPOSessionCallbacks`オブジェクトに渡す必要があります。全GGPOSessionCallbackファンクションは実行をしなければなりません。詳細は以下を参照してください。例として、ポート8001にバインドしている別プレイヤーと同じホストで新しいセッションをスタートする場合、次のようになります:
|
||||
`GGPOSession`オブジェクトはGGPOフレームワークへのインターフェイスです。ローカル、IPアドレス、対戦したいプレイヤーをバインドするためのポートを渡す`ggponet_start_session`ファンクションを使用して、作成します。またセッションが1プレイヤー側、2プレイヤー側いずれの場合でも、ゲームステートを管理するコールバック関数で満たされた`GGPOSessionCallbacks`オブジェクトに渡す必要があります。全GGPOSessionCallbackファンクションは実行をしなければなりません。詳細は以下を参照してください。
|
||||
例として、ポート8001にバインドしている別プレイヤーと同じホストで新しいセッションをスタートする場合、次のようになります:
|
||||
|
||||
```
|
||||
GGPOSession ggpo;
|
||||
@@ -58,9 +55,10 @@ GGPOは新旧のゲームエンジンと簡単にインターフェイスでき
|
||||
8001); // our local udp port
|
||||
```
|
||||
|
||||
`GGPOSession`オブジェクトは1つのゲームセッションだけに用いられるべきです。 別の相手と接続する必要がある場合、`ggpo_close_session`を使用して既存のオブジェクトを閉じ、新たに以下のものを始めてください:
|
||||
|
||||
|
||||
`GGPOSession`オブジェクトは1つのゲームセッションだけに用いられるべきです。別の相手と接続する必要がある場合、`ggpo_close_session`を使用して既存のオブジェクトを閉じ、新たに以下のものを始めてください:
|
||||
|
||||
```
|
||||
/* Close the current session and start a new one */
|
||||
ggpo_close_session(ggpo);
|
||||
@@ -87,14 +85,15 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
|
||||
|
||||
### ローカルと遠隔プレイヤーによる入力の同期
|
||||
|
||||
入力の同期は各ゲームフレームの最初に行われます。各ローカルプレイヤーの`ggpo_add_local_input`の呼び出しと、 遠隔プレイヤーの入力を取得するために`ggpo_synchronize_input`の呼び出しをすることで行われます。`ggpo_synchronize_inputs`の戻り値を必ず確認するようにしてください。`GGPO_OK`以外の値を返す場合、ゲームステートを次に進めるべきではありません。わずかなあいだ、遠隔プレイヤーからパケットを受信していなかったり、内部で行われる予測の限界に達することがあるために、こういったことは頻繁に発生します。
|
||||
入力の同期は各ゲームフレームの最初に行われます。各ローカルプレイヤーの`ggpo_add_local_input`の呼び出しと、 遠隔プレイヤーの入力を取得するために`ggpo_synchronize_input`の呼び出しをすることで行われます。
|
||||
`ggpo_synchronize_inputs`の戻り値を必ず確認するようにしてください。`GGPO_OK`以外の値を返す場合、ゲームステートを次に進めるべきではありません。わずかなあいだ、遠隔プレイヤーからパケットを受信していなかったり、内部で行われる予測の限界に達することがあるために、こういったことは頻繁に発生します。
|
||||
|
||||
例えば、ローカルゲームのコードがこのような場合:
|
||||
|
||||
```
|
||||
GameInputs &p1, &p2;
|
||||
GetControllerInputs(0, &p1); /* read p1’s controller inputs */
|
||||
GetControllerInputs(1, &p2); /* read p2’s controller inputs */
|
||||
GetControllerInputs(0, &p1); /* read p1's controller inputs */
|
||||
GetControllerInputs(1, &p2); /* read p2's controller inputs */
|
||||
AdvanceGameState(&p1, &p2, &gamestate); /* send p1 and p2 to the game */
|
||||
```
|
||||
|
||||
@@ -122,8 +121,7 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
|
||||
}
|
||||
```
|
||||
|
||||
ロールバック中に発生したものでも、フレームごとに`ggpo_synchronize_inputs`を呼び出す必要があります。 ゲームステートを進めるためには、ローカルコントローラーから得られた値を読むのではなく、常に`ggpo_synchronize_inputs`から返ってくる値を使用してください。ロールバック中、`ggpo_synchronize_inputs`は前のフレームに用いられた値とともに、`ggpo_add_local_input`に渡された値を置き換えます。 また、ロールバックの影響を緩和するためにローカルプレイヤー向けの入力遅延を加えた場合、 フレーム遅延が終えるまで`ggpo_add_local_input`に渡される入力は`ggpo_synchronize_inputs`に返されません。
|
||||
|
||||
ロールバック中に発生したものでも、フレームごとに`ggpo_synchronize_inputs`を呼び出す必要があります。ゲームステートを進めるためには、ローカルコントローラーから得られた値を読むのではなく、常に`ggpo_synchronize_inputs`から返ってくる値を使用してください。ロールバック中、`ggpo_synchronize_inputs`は前のフレームに用いられた値とともに、`ggpo_add_local_input`に渡された値を置き換えます。また、ロールバックの影響を緩和するためにローカルプレイヤー向けの入力遅延を加えた場合、 フレーム遅延が終えるまで`ggpo_add_local_input`に渡される入力は`ggpo_synchronize_inputs`に返されません。
|
||||
|
||||
### セーブ、ロード、およびフリーコールバックの実装
|
||||
|
||||
@@ -165,8 +163,7 @@ ggpo_free_buffer(void *buffer)
|
||||
|
||||
### 残っているコールバックの実装
|
||||
|
||||
先ほど挙げたように、`GGPOSessionCallbacks`構造内にはオプショナルコールバックがありません。 最低でも`return true`である必要がありますが、残っているコールバックは即座に実行される必要がありません。詳しい情報はggponet.hのコメントをご覧ください。
|
||||
|
||||
先ほど挙げたように、`GGPOSessionCallbacks`構造内にはオプショナルコールバックがありません。最低でも`return true`である必要がありますが、残っているコールバックは即座に実行される必要がありません。詳しい情報はggponet.hのコメントをご覧ください。
|
||||
|
||||
### GGPOアドバンスとアイドル関数の呼び出し
|
||||
|
||||
@@ -174,7 +171,6 @@ ggpo_free_buffer(void *buffer)
|
||||
|
||||
GGPOは内部記録のパケットを送受信するために、一定の時間が必要になります。GGPOに許可したミリ秒単位で、最低でもフレームごとに1回は`ggpo_idle function`を呼び出す必要があります。
|
||||
|
||||
|
||||
## アプリケーションのチューニング: フレーム遅延 vs 投機的実行
|
||||
|
||||
遅延を感じさせないようにするために、GGPOはフレーム遅延と投機的実行の両方を用います。アプリ開発者が遅延入力のフレーム数はどの程度にするか、については選択ができます。もしゲームのフレーム数よりパケットの送信に時間がかかる場合は、GGPOが投機的実行を用いて残りの遅延を感じさせないようにします。もし希望すれば、ゲーム中でもこの数字を変更できます。フレーム遅延の適切な値はゲームによって依存します。以下は役に立つヒントです。
|
||||
@@ -183,7 +179,6 @@ GGPOは内部記録のパケットを送受信するために、一定の時間
|
||||
|
||||
フレーム遅延を高く設定するもう一つの理由は、ロールバック中に発生しうるグリッチ(不具合)を排除することにあります。ロールバックが長くなればなるほど、間違った予測フレームを一時的に表示したことで、本来ないシーンを継ぎ接ぎした様子が表示される可能性が高くなるからです。例えば、ユーザーがボタンを押した瞬間にちょうど2フレーム分全画面がフラッシュする仕様のゲームがあったとします。フレーム遅延を1に設定し、パケット送信に4フレームかかったとすると、この場合はロールバックは約3フレーム分(4 - 1 = 3)になります。フラッシュがロールバックの最初のフレームに発生した場合、2フレームのフラッシュはロールバックで消えてしまい、遠隔で遊ぶプレイヤーはフラッシュの演出が見えなくなってしまいます。この場合、さらに高いフレーム遅延値を設定するか、ロールバック発生後までフラッシュを遅らせるようビデオレンダラーを再設計するか、のどちらかになります。
|
||||
|
||||
|
||||
## サンプルアプリケーション
|
||||
|
||||
ソースディレクトリ内のVector Warアプリケーションでは、2つのクライアントを同期するGGPOが搭載されています。コマンドライン引数は
|
||||
@@ -192,26 +187,22 @@ GGPOは内部記録のパケットを送受信するために、一定の時間
|
||||
vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>) for each player
|
||||
```
|
||||
|
||||
2~4プレイヤーゲームの始め方についての例は、binディレクトリにある.batファイルをご覧ください。
|
||||
|
||||
2~4プレイヤーゲームの始め方についての例は、binディレクトリにある.cmdファイルをご覧ください。
|
||||
|
||||
## ベストプラクティスとトラブルシューティング
|
||||
|
||||
以下はGGPOへアプリを移植する際に検討をしたい、推薦できるベストプラクティスの一覧です。最初の段階からゲームを始めなくとも、ここで推奨している多くは簡単にまねることができます。多くのアプリケーションは推薦している以下の手法に準拠しています。
|
||||
|
||||
|
||||
### 非ゲームステートとゲームステートを分ける
|
||||
|
||||
GGPOは定期的にゲームにおける全ステートのセーブとロードを要求します。多くのゲームにとって、セーブが必要となるステートはゲーム全体のなかでも小さな要素にあたります。ビデオやオーディオレンダラー、テーブルの検索、テクスチャ―サウンドデータ、コードセグメントは大半の場合、フレームからフレームまで一定か、ゲームステートの計算には関与しません。これらはセーブや復元する必要がありません。
|
||||
|
||||
ゲームステートから非ゲームステートを出来る限り分けましょう。例えば、全ゲームステートをC構造体へカプセル化を考えるかもしれません。この二つは、ゲームステートであるもの、そうでないもの、セーブやロード呼び出しの実行に必要でないものをはっきりと分けてくれます(さらなる情報はリファレンスガイドをご覧ください)。
|
||||
|
||||
|
||||
### ゲームステートを進める際の固定のタイムクオンタムを定義する
|
||||
|
||||
GGPOは時折、ロールバックやフレームごとにアプリに対してシングルステップ実行をする必要があります。もしあなたのゲームステートが変数のチックレートで進めているのであれば難しい実行です。レンダーループがそうでなくとも、ゲームステートの進行はフレームにつき固定のタイムクオンタムでするようにしてください。
|
||||
|
||||
|
||||
### ゲームの一連の流れをレンダリングするところから、ゲームステートのアップデートを分離する
|
||||
|
||||
GGPはロールバック中、事前フレームコールバックを何度も呼び出します。ロールバックが終わるまで、ロールバック中に発生したエフェクトやサウンドは遅らせる必要があります。これはレンダーステートからゲームステートを分離することで簡単に行うことができます。そうできたら、以下のようになるでしょう。
|
||||
@@ -232,29 +223,24 @@ GGPはロールバック中、事前フレームコールバックを何度も
|
||||
|
||||
言い換えると、ゲームステートは入力のみで決定され、レンダリングコードは現在のゲームステートによって動作をします。レンダリングなしで一連の入力をもとに簡単にゲームステートを進める方法であるべきです。
|
||||
|
||||
|
||||
### ゲームステートの進行は決定性であること
|
||||
|
||||
ゲームステートを特定したら、必ず次のゲームステートはゲーム入力のみで計算されるようにしてください。ゲームステートと入力がすべて正しく特定できたなら、このことは自然と発生しますが、時として手が込む作業です。以下は見落としやすい内容です。
|
||||
|
||||
|
||||
#### 乱数ジェネレーターに気を付ける
|
||||
|
||||
次のゲームステートを計算するうえで、多くのゲームは乱数を使用します。もし乱数を使っていたら、それが両プレイヤーがフレーム0の時に乱数ジェネレーターのシードが同一であること、そしてあなたのゲームステート内に乱数ジェネレーターのステートが含まれていること、といった完全に決定性であることを必ず確認してください。この両方を確認すれば、GGPOが特定のフレームでロールバックが何回も必要になろうとも、そのフレームで生成された乱数は必ず同一になります。
|
||||
|
||||
|
||||
#### 外部時間ソース(壁時計時刻)に気を付ける
|
||||
|
||||
ゲームステートの計算にその日の現在時刻を用いる場合は注意してください。このことでゲームに影響を与える、または他のゲームステートを導く可能性があります(例: タイマーを乱数ジェネレーターのシードに用いる)。2つのコンピューターもしくはゲームコンソールの時刻は、ほぼ同期することはなく、ゲームステート計算に時刻を使用すると同期の問題につながります。ゲームステートに時刻の使用する、またはフレームに入力する一部として、プレイヤーの1人のために現在時刻を含める、そして計算に時刻を常に用いるといったことは排除するべきです。
|
||||
|
||||
非ゲームステート計算における外部時刻ソースの使用は構いません(例: 画面上の効果時間を計算、またはオーディオサンプルの減衰)。
|
||||
|
||||
|
||||
### ダングリングリファレンスに気を付ける
|
||||
|
||||
ゲームステートが動的に割り当てられたメモリを含んでいる場合、データをセーブ/ロードする際には、ポインタをリベースするセーブとロードファンクションに十分気を付けてください。緩和をする一つの方法として、ポインタの代わりに割り当てられたメモリを参照するため、ベースとオフセットを使用します。これでリベースが必要なポインタの数が大幅に削減できます。
|
||||
|
||||
|
||||
### 静的変数、またはほかの隠れたステートに気を付ける
|
||||
|
||||
あなたのゲームで用いられている言語は、全ステートを追跡するのを難しくさせている特徴があるかもしれません。Cにある静的自動変数はこの挙動の一例にあたります。すべてのロケーションを追跡して、セーブできるフォームへ変換する必要があります。例として、この2つを比較してください。まずはこちらから:
|
||||
@@ -296,7 +282,6 @@ GGPOにあなたのアプリを移植したら、リークがありやすいゲ
|
||||
|
||||
ゲームコードを書き込んでいる時に開発システムで継続的に同期テストを実行することで、非同期を発生するバグをすぐに見つけることができます。
|
||||
|
||||
|
||||
## さらに詳しく知りたい方は
|
||||
|
||||
この文章はGGPOの基本的な特徴を紹介しています。さらに知りたい方は、ggponet.hヘッダーにあるコメント、そしてコードを直接読むことをお勧めします。それではみなさん頑張ってください!
|
||||
|
||||
@@ -10,11 +10,11 @@ Your game probably has many moving parts. GGPO only depends on these two:
|
||||
|
||||
- **Game Inputs** are the set of things which modify the game state. These obviously include the joystick and button presses done by the player, but can include other non-obvious inputs as well. For example, if your game uses the current time of day to calculate something in the game, the current time of day at the beginning of a frame is also an input.
|
||||
|
||||
There are many other things in your game engine that are neither game state nor inputs. For example, your audio and video renderers are not game state since they don’t have an effect on the outcome of the game. If you have a special effects engine that’s generating effects that do not have an impact on the game, they can be excluded from the game state as well.
|
||||
There are many other things in your game engine that are neither game state nor inputs. For example, your audio and video renderers are not game state since they don't have an effect on the outcome of the game. If you have a special effects engine that's generating effects that do not have an impact on the game, they can be excluded from the game state as well.
|
||||
|
||||
## Using State and Inputs for Synchronization
|
||||
|
||||
Each player in a GGPO networked game has a complete copy of your game running. GGPO needs to keep both copies of the game state in sync to ensure that both players are experiencing the same game. It would be much too expensive to send an entire copy of the game state between players every frame. Instead GGPO sends the players’ inputs to each other and has each player step the game forward. In order for this to work, your game engine must meet three criteria:
|
||||
Each player in a GGPO networked game has a complete copy of your game running. GGPO needs to keep both copies of the game state in sync to ensure that both players are experiencing the same game. It would be much too expensive to send an entire copy of the game state between players every frame. Instead GGPO sends the players' inputs to each other and has each player step the game forward. In order for this to work, your game engine must meet three criteria:
|
||||
|
||||
- The game simulation must be fully deterministic. That is, for any given game state and inputs, advancing the game state by exactly 1 frame must result in identical game states for all players.
|
||||
- The game state must be fully encapsulated and serializable.
|
||||
@@ -30,7 +30,7 @@ GGPO is designed to be easy to interface with new and existing game engines. It
|
||||
|
||||
### Creating the GGPOSession Object
|
||||
|
||||
The `GGPOSession` object is your interface to the GGPO framework. Create one with the `ggponet_start_session` function passing the port to bind to locally and the IP address and port of the player you’d like to play against. You should also pass in a `GGPOSessionCallbacks` object filled in with your game’s callback functions for managing game state and whether this session is for player 1 or player 2. All `GGPOSessionCallback` functions must be implemented. See the reference for more details.
|
||||
The `GGPOSession` object is your interface to the GGPO framework. Create one with the `ggponet_start_session` function passing the port to bind to locally and the IP address and port of the player you'd like to play against. You should also pass in a `GGPOSessionCallbacks` object filled in with your game's callback functions for managing game state and whether this session is for player 1 or player 2. All `GGPOSessionCallback` functions must be implemented. See the reference for more details.
|
||||
For example, to start a new session on the same host with another player bound to port 8001, you would do:
|
||||
|
||||
```
|
||||
@@ -65,6 +65,7 @@ The `GGPOSession` object should only be used for a single game session. If you
|
||||
```
|
||||
|
||||
### Sending Player Locations
|
||||
|
||||
When you created the GGPOSession object passed in the number of players participating in the game, but didn't actually describe how to contact them. To do so, call the `ggpo_add_player` function with a `GGPOPlayer` object describing each player. The following example show how you might use ggpo_add_player in a 2 player game:
|
||||
|
||||
```
|
||||
@@ -83,6 +84,7 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
|
||||
```
|
||||
|
||||
### Synchronizing Local and Remote Inputs
|
||||
|
||||
Input synchronization happens at the top of each game frame. This is done by calling `ggpo_add_local_input` for each local player and `ggpo_synchronize_input` to fetch the inputs for remote players.
|
||||
Be sure to check the return value of `ggpo_synchronize_inputs`. If it returns a value other than `GGPO_OK`, you should not advance your game state. This usually happens because GGPO has not received packets from the remote player in a while and has reached its internal prediction limit.
|
||||
|
||||
@@ -90,8 +92,8 @@ For example, if your code looks like this currently for a local game:
|
||||
|
||||
```
|
||||
GameInputs &p1, &p2;
|
||||
GetControllerInputs(0, &p1); /* read p1’s controller inputs */
|
||||
GetControllerInputs(1, &p2); /* read p2’s controller inputs */
|
||||
GetControllerInputs(0, &p1); /* read p1's controller inputs */
|
||||
GetControllerInputs(1, &p2); /* read p2's controller inputs */
|
||||
AdvanceGameState(&p1, &p2, &gamestate); /* send p1 and p2 to the game */
|
||||
```
|
||||
|
||||
@@ -122,6 +124,7 @@ You should change it to read as follows:
|
||||
You should call `ggpo_synchronize_inputs` every frame, even those that happen during a rollback. Make sure you always use the values returned from `ggpo_synchronize_inputs` rather than the values you've read from the local controllers to advance your game state. During a rollback `ggpo_synchronize_inputs` will replace the values passed into `ggpo_add_local_input` with the values used for previous frames. Also, if you've manually added input delay for the local player to smooth out the effect of rollbacks, the inputs you pass into `ggpo_add_local_input` won't actually be returned in `ggpo_synchronize_inputs` until after the frame delay.
|
||||
|
||||
### Implementing your save, load, and free Callbacks
|
||||
|
||||
GGPO will use the `load_game_state` and `save_game_state` callbacks to periodically save and restore the state of your game. The `save_game_state` function should create a buffer containing enough information to restore the current state of the game and return it in the `buffer` out parameter. The `load_game_state` function should restore the game state from a previously saved buffer. For example:
|
||||
|
||||
```
|
||||
@@ -159,42 +162,50 @@ ggpo_free_buffer(void *buffer)
|
||||
```
|
||||
|
||||
### Implementing Remaining Callbacks
|
||||
|
||||
As mentioned previously, there are no optional callbacks in the `GGPOSessionCallbacks` structure. They all need to at least `return true`, but the remaining callbacks do not necessarily need to be implemented right away. See the comments in `ggponet.h` for more information.
|
||||
|
||||
### Calling the GGPO Advance and Idle Functions
|
||||
We're almost done. Promise. The last step is notify GGPO every time your gamestate finishes advancing by one frame. Just call `ggpo_advance_frame` after you’ve finished one frame but before you’ve started the next.
|
||||
|
||||
GGPO also needs some amount of time to send and receive packets do its own internal bookkeeping. At least once per-frame you should call the `ggpo_idle` function with the number of milliseconds you’re allowing GGPO to spend.
|
||||
We're almost done. Promise. The last step is notify GGPO every time your gamestate finishes advancing by one frame. Just call `ggpo_advance_frame` after you've finished one frame but before you've started the next.
|
||||
|
||||
GGPO also needs some amount of time to send and receive packets do its own internal bookkeeping. At least once per-frame you should call the `ggpo_idle` function with the number of milliseconds you're allowing GGPO to spend.
|
||||
|
||||
## Tuning Your Application: Frame Delay vs. Speculative Execution
|
||||
GGPO uses both frame delay and speculative execution to hide latency. It does so by allowing the application developer the choice of how many frames that they’d like to delay input by. If it takes more time to transmit a packet than the number of frames specified by the game, GGPO will use speculative execution to hide the remaining latency. This number can be tuned by the application mid-game if you so desire. Choosing a proper value for the frame delay depends very much on your game. Here are some helpful hints.
|
||||
|
||||
GGPO uses both frame delay and speculative execution to hide latency. It does so by allowing the application developer the choice of how many frames that they'd like to delay input by. If it takes more time to transmit a packet than the number of frames specified by the game, GGPO will use speculative execution to hide the remaining latency. This number can be tuned by the application mid-game if you so desire. Choosing a proper value for the frame delay depends very much on your game. Here are some helpful hints.
|
||||
|
||||
In general you should try to make your frame delay as high as possible without affecting the qualitative experience of the game. For example, a fighting game requires pixel perfect accuracy, excellent timing, and extremely tightly controlled joystick motions. For this type of game, any frame delay larger than 1 can be noticed by most intermediate players, and expert players may even notice a single frame of delay. On the other hand, board games or puzzle games which do not have very strict timing requirements may get away with setting the frame latency as high as 4 or 5 before users begin to notice.
|
||||
|
||||
Another reason to set the frame delay high is to eliminate the glitching that can occur during a rollback. The longer the rollback, the more likely the user is to notice the discontinuities caused by temporarily executing the incorrect prediction frames. For example, suppose your game has a feature where the entire screen will flash for exactly 2 frames immediately after the user presses a button. Suppose further that you’ve chosen a value of 1 for the frame latency and the time to transmit a packet is 4 frames. In this case, a rollback is likely to be around 3 frames (4 – 1 = 3). If the flash occurs on the first frame of the rollback, your 2-second flash will be entirely consumed by the rollback, and the remote player will never get to see it! In this case, you’re better off either specifying a higher frame latency value or redesigning your video renderer to delay the flash until after the rollback occurs.
|
||||
Another reason to set the frame delay high is to eliminate the glitching that can occur during a rollback. The longer the rollback, the more likely the user is to notice the discontinuities caused by temporarily executing the incorrect prediction frames. For example, suppose your game has a feature where the entire screen will flash for exactly 2 frames immediately after the user presses a button. Suppose further that you've chosen a value of 1 for the frame latency and the time to transmit a packet is 4 frames. In this case, a rollback is likely to be around 3 frames (4 – 1 = 3). If the flash occurs on the first frame of the rollback, your 2-second flash will be entirely consumed by the rollback, and the remote player will never get to see it! In this case, you're better off either specifying a higher frame latency value or redesigning your video renderer to delay the flash until after the rollback occurs.
|
||||
|
||||
## Sample Application
|
||||
|
||||
The Vector War application in the source directory contains a simple application which uses GGPO to synchronize the two clients. The command line arguments are:
|
||||
|
||||
```
|
||||
vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>) for each player
|
||||
```
|
||||
|
||||
See the .bat files in the bin directory for examples on how to start 2, 3, and 4 player games.
|
||||
See the .cmd files in the bin directory for examples on how to start 2, 3, and 4 player games.
|
||||
|
||||
## Best Practices and Troubleshooting
|
||||
Below is a list of recommended best practices you should consider while porting your application to GGPO. Many of these recommendations are easy to follow even if you’re not starting a game from scratch. Most applications will already conform to most of the recommendations below.
|
||||
|
||||
Below is a list of recommended best practices you should consider while porting your application to GGPO. Many of these recommendations are easy to follow even if you're not starting a game from scratch. Most applications will already conform to most of the recommendations below.
|
||||
|
||||
### Isolate Game State from Non-Game State
|
||||
|
||||
GGPO will periodically request that you save and load the entire state of your game. For most games the state that needs to be saved is a tiny fraction of the entire game. Usually the video and audio renderers, look up tables, textures, sound data and your code segments are either constant from frame to frame or not involved in the calculation of game state. These do not need to be saved or restored.
|
||||
|
||||
You should isolate non-game state from the game state as much as possible. For example, you may consider encapsulating all your game state into a single C structure. This both clearly delineates what is game state and was is not and makes it trivial to implement the save and load callbacks (see the Reference Guide for more information).
|
||||
|
||||
### Define a Fixed Time Quanta for Advancing Your Game State
|
||||
|
||||
GGPO will occasionally need to rollback and single-step your application frame by frame. This is difficult to do if your game state advances by a variable tick rate. You should try to make your game state advanced by a fixed time quanta per frame, even if your render loop does not.
|
||||
|
||||
### Separate Updating Game State from Rendering in Your Game Loop
|
||||
GGPO will call your advance frame callback many times during a rollback. Any effects or sounds which are genearted during the rollback need to be deferred until after the rollback is finished. This is most easily accomplished by separating your game state from your render state. When you’re finished, your game loop may look something like this:
|
||||
|
||||
GGPO will call your advance frame callback many times during a rollback. Any effects or sounds which are genearted during the rollback need to be deferred until after the rollback is finished. This is most easily accomplished by separating your game state from your render state. When you're finished, your game loop may look something like this:
|
||||
|
||||
```
|
||||
Bool finished = FALSE;
|
||||
@@ -213,20 +224,25 @@ GGPO will call your advance frame callback many times during a rollback. Any ef
|
||||
In other words, your game state should be determined solely by the inputs, your rendering code should be driven by the current game state, and you should have a way to easily advance the game state forward using a set of inputs without rendering.
|
||||
|
||||
### Make Sure Your Game State Advances Deterministically
|
||||
|
||||
Once you have your game state identified, make sure the next game state is computed solely from your game inputs. This should happen naturally if you have correctly identified all the game state and inputs, but it can be tricky sometimes. Here are some things which are easy to overlook:
|
||||
|
||||
#### Beware of Random Number Generators
|
||||
|
||||
Many games use random numbers in the computing of the next game state. If you use one, you must ensure that they are fully deterministic, that the seed for the random number generator is same at frame 0 for both players, and that the state of the random number generator is included in your game state. Doing both of these will ensure that the random numbers which get generated for a particular frame are always the same, regardless of how many times GGPO needs to rollback to that frame.
|
||||
|
||||
#### Beware of External Time Sources (aka. Wall clock time)
|
||||
|
||||
Be careful if you use the current time of day in your game state calculation. This may be used for an effect on the game or to derive other game state (e.g. using the timer as a seed to the random number generator). The time on two computers or game consoles is almost never in sync and using time in your game state calculations can lead to synchronization issues. You should either eliminate the use of time in your game state or include the current time for one of the players as part of the input to a frame and always use that time in your calculations.
|
||||
|
||||
The use of external time sources in non-gamestate calculations is fine (e.g. computing the duration of effects on screen, or the attenuation of audio samples).
|
||||
|
||||
### Beware of Dangling References
|
||||
|
||||
If your game state contains any dynamically allocated memory be very careful in your save and load functions to rebase your pointers as you save and load your data. One way to mitigate this is to use a base and offset to reference allocated memory instead of a pointer. This can greatly reduce the number of pointers you need to rebase.
|
||||
|
||||
### Beware of Static Variables or Other Hidden State
|
||||
|
||||
The language your game is written in may have features which make it difficult to track down all your state. Static automatic variables in C are an example of this behavior. You need to track down all these locations and convert them to a form which can be saved. For example, compare:
|
||||
|
||||
```
|
||||
@@ -259,11 +275,13 @@ To:
|
||||
```
|
||||
|
||||
### Use the GGPO SyncTest Feature. A Lot.
|
||||
Once you’ve ported your application to GGPO, you can use the `ggpo_start_synctest` function to help track down synchronization issues which may be the result of leaky game state.
|
||||
|
||||
Once you've ported your application to GGPO, you can use the `ggpo_start_synctest` function to help track down synchronization issues which may be the result of leaky game state.
|
||||
|
||||
The sync test session is a special, single player session which is designed to find errors in your simulation's determinism. When running in a synctest session, GGPO will execute a 1 frame rollback for every frame of your game. It compares the state of the frame when it was executed the first time to the state executed during the rollback, and raises an error if they differ. If you used the `ggpo_log` function during your game's execution, you can diff the log of the initial frame vs the log of the rollback frame to track down errors.
|
||||
|
||||
By running synctest on developer systems continuously when writing game code, you can identify desync causing bugs immediately after they're introduced.
|
||||
|
||||
## Where to Go from Here
|
||||
|
||||
This document describes the most basic features of GGPO. To learn more, I recommend starting with reading the comments in the `ggponet.h` header and just diving into the code. Good luck!
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
|
||||
従来の技術はプレイヤーの入力に遅延を織り込んで通信を行っており、その結果反応が遅く、ラグを感じるプレイ感になっていました。ロールバックネットワーキングは入力予測と投機的実行を行って、プレイヤーの入力を即座に送信するため、遅延を感じさせないネット環境をもたらします。ロールバックがあれば、タイミングや相手の動きや効果音に対する反応、指が覚えている入力、これらオフラインで行えた内容が、そのままオンラインでも行えます。GGPOネットワーキングSDKは、ロールバックネットワーキングを新作や発売されているゲームに極力簡単に組み込めるよう作られています。
|
||||
|
||||
|
||||
# 仕組み
|
||||
|
||||
ロールバックネットワーキングは決定的P2Pエンジンに統合できるよう設計されています。完全に決定的なエンジンなら、同じ入力をした場合にゲームは必ず同じ内容のプログラム再生をします。その内容を実現する一つの方法としては、ネットワーク上の全プレイヤーと入力のやりとりをする方法があげられますが、これは全プレイヤーがピアから入力を全て受け取った時にのみゲームプレイロジックが1フレームだけ実行される形になります。この方法ではゲーム内でキャラの動きがぎくしゃくし、反応の悪いゲーム内容になりがちです。ネットワークを介して入力を受け取る時間が長くなるほど、ゲーム展開も遅くなってしまいます。
|
||||
|
||||
|
||||
## 入力遅延を用いたネットワーキング
|
||||
|
||||
### 理論上では…
|
||||
### 理論上は…
|
||||
|
||||
下の図を見てください。2つのクライアントが遅延0msの理想的なネットワークで同期されている図になっています。1プレイヤー側の入力が青、2プレイヤー側の入力は赤、ネットワーク層は緑です。黒の矢印は入力がシステム内で送信され、ゲームステートが推移する流れを表します。各フレームは破線で区切られています。図は1プレイヤー側から見たものになっていますが、2プレイヤー側も全く同じ手順になっています。
|
||||
|
||||
@@ -20,7 +18,6 @@
|
||||
|
||||
1プレイヤーの入力は、ネットワーク層によって2プレイヤーの入力とマージされ、ゲームエンジンに送信されます。エンジンはその入力を用いて現在のフレームのゲームステートを変更します。2プレイヤー側も同様に自分と1プレイヤーの入力をマージしてゲームエンジンに送信します。プレイヤーの入力に応じたロジックを適用し、過去のフレームのゲームステートを変更しながら、フレームごとにゲームが進行します。1プレイヤーと2プレイヤー両方が同じゲームステートで、それぞれのエンジンに送信される入力が同じなので、両プレイヤーのゲームステートは毎フレーム同期されたままになります。
|
||||
|
||||
|
||||
### 実際は…
|
||||
|
||||
理想的なネットワークの例では、パケットがネットワークを介して即時に送信されるものとされていますが、現実はそう甘くはありません。一般的なブロードバンド接続では、プレイヤー間の距離や回線の品質に応じて、パケットの送信に5~150msかかります。ゲームが1秒間に60フレームで実行されるとするならば、遅延は1~9フレーム相当になります。
|
||||
@@ -31,7 +28,6 @@
|
||||
|
||||
この例では、パケットの送信に3フレームかかります。2プレイヤーによって遠隔から送信された入力は、1フレーム目に1プレイヤー側に届かず、3フレーム後になるまでゲーム機に届きません。1プレイヤー側のゲームエンジンは入力を受信するまでゲームを進めることができないので、1フレーム目を3フレーム遅延せざるを得なくなります。続きのフレームも同様に3フレームの遅延が発生します。ネットワーク層は両プレイヤー間で送信されるパケットの最長転送時間だけ、マージされた入力を遅延せざるを得なくなります。理想的なネットワーク環境を除いては、大半のゲームジャンルにおいてこのラグはプレイ感に大きく影響を与えることとなります。
|
||||
|
||||
|
||||
## ロールバックネットワーキングで入力遅延を取り除く
|
||||
|
||||
### 投機的実行
|
||||
@@ -44,15 +40,14 @@ GGPOは遠隔のプレイヤーから入力が届くのを待つ代わりに、
|
||||
GGPOの予測が完璧であれば、オンラインで遊ぶユーザー体験はオフラインと同一のものになります。もちろん、未来を予測することは誰にもできません!GGPOも2プレイヤーの入力を間違って予測することがあります。上の図をもう一度見てください。もしGGPOが1フレーム目に2プレイヤーに間違った入力を送信したらどうなるでしょうか。1プレイヤー側に表示される2プレイヤーの入力は、2プレイヤー側で表示されるものと異なってしまいます。両サイドのゲームは同期を失い、プレイヤーは違ったゲーム画面を見ながら相手の動きに反応することになります。同期のズレは、1プレイヤー側が2プレイヤーの正しい入力が届く4フレーム目まで検出することができませんが、それでは遅すぎます。
|
||||
そういうことから、GGPOの手法は「投機的実行(speculative execution)」と呼ばれます。遊んでいるプレイヤーがその時に見ているものは正しいかもしれませんが、そうでないこともあります。GGPOが遠隔プレイヤーの入力を誤って予測した場合、次のフレームへ進める前にエラーを修正する必要があります。次の例では、その方法を説明します。
|
||||
|
||||
|
||||
### 投機的実行エラーをロールバックで修正する
|
||||
|
||||
GGPOは遠隔プレイヤーの入力を間違って予測する度に、ロールバックを使ってクライアントを再同期します。「ロールバック」という単語は、ステートを巻き戻し、プレイヤーの入力に関する、より正しく新しい情報を元に結果を予測する過程を指します。前のセクションでは、遠隔の入力1における予測したフレームが間違っていたらどうなるか、ということについて考えました。それでは、GGPOがエラーを修正する過程を見てみましょう。
|
||||
|
||||

|
||||
|
||||
GGPOは遠隔の入力を受信したら、その都度前回のフレームで予測した品質をチェックします。先程触れたように、GGPOは4フレーム目まで2プレイヤー側の入力が届きません。4フレーム目で、GGPOは以前に予測した入力とネットワークから受信した入力が一致しないことに気付きます。両サイドのゲームを再同期するため、GGPOは3フレーム分の誤った入力によって発生したダメージや間違いを取り消す必要があります。誤って予測した入力を送信する前のフレームまで戻るよう、ゲームエンジンに要求します(つまり過去のステートまで「ロールバック」します)。以前のステートを復元したら、GGPOはエンジンに正しい入力で1フレーム進めるよう要求します。このフレームは水色で示しています。ゲームエンジンはこのフレームをユーザーに見えない形で出来る限り素早く進める必要があります。例えば、ビデオレンダラーはこのフレームを画面に描写するべきではありません。オーディオレンダラーは原則、音声を生成し続けるべきですが、ロールバックが終わるまでレンダーすべきではなく、サンプルが生成されたフレームを引いた現在のフレームであるnフレームでサンプルがスタートする必要があります。エンジンがGGPOがエラーを見つける前のフレームまで到達したら、GGPOはロールバックモードを止め、ゲームを通常どおり進めることを許可します。図の5フレームと6フレーム目はGGPOの予測が正しく行われた場合を示しています。ゲームステートが正しいので、ロールバックをする理由はありません。
|
||||
|
||||
GGPOは遠隔の入力を受信したら、その都度前回のフレームで予測した品質をチェックします。先程触れたように、GGPOは4フレーム目まで2プレイヤー側の入力が届きません。4フレーム目で、GGPOは以前に予測した入力とネットワークから受信した入力が一致しないことに気付きます。両サイドのゲームを再同期するため、GGPOは3フレーム分の誤った入力によって発生したダメージや間違いを取り消す必要があります。誤って予測した入力を送信する前のフレームまで戻るよう、ゲームエンジンに要求します(つまり過去のステートまで「ロールバック」します)。以前のステートを復元したら、GGPOはエンジンに正しい入力で1フレーム進めるよう要求します。このフレームは水色で示しています。ゲームエンジンはこのフレームをユーザーに見えない形で出来る限り素早く進める必要があります。例えば、ビデオレンダラーはこのフレームを画面に描写するべきではありません。オーディオレンダラーは原則、音声を生成し続けるべきですが、ロールバックが終わるまでレンダーすべきではなく、サンプルが生成されたフレームを引いた現在のフレームであるnフレームでサンプルがスタートする必要があります。
|
||||
エンジンがGGPOがエラーを見つける前のフレームまで到達したら、GGPOはロールバックモードを止め、ゲームを通常どおり進めることを許可します。図の5フレームと6フレーム目はGGPOの予測が正しく行われた場合を示しています。ゲームステートが正しいので、ロールバックをする理由はありません。
|
||||
|
||||
# コード構造
|
||||
|
||||
@@ -60,42 +55,34 @@ GGPOは遠隔の入力を受信したら、その都度前回のフレームで
|
||||
|
||||

|
||||
|
||||
|
||||
## GGPOインタフェース(GGPO Interface)
|
||||
|
||||
GGPOインターフェイスはP2Pと同期テストバックエンド間の詳細な実装を抽象化しています。適切なバックエンドは`ggpo_start_session`か`ggpo_start_synctest`エントリーポイントを呼び出した時に、自動的に生成されます。
|
||||
|
||||
|
||||
## P2Pバックエンド(P2P Backend)
|
||||
|
||||
P2Pバックエンドはプレイヤー間でゲームを調整します。`ggpo_start_session` APIの呼び出しによって生成されます。大きな情報の処理の大半は含まれているヘルパークラスによって行われます。
|
||||
|
||||
|
||||
## ポーリングオブジェクト(Poll Object)
|
||||
|
||||
(図にはありません)ポーリングオブジェクトはコード内で他のオブジェクトによって用いられる登録方式です。待機可能なオブジェクトが準備できたときに通知とタイマーを送信します。例としてUDPバックエンドは新たなパケットが到着したときに、通知を受信するためポーリングオブジェクトを使用します。
|
||||
|
||||
|
||||
## 同期オブジェクト(Sync Object)
|
||||
|
||||
同期オブジェクトはゲームステートのnフレームを追跡するために用いられます。埋め込まれた予測(prediction)オブジェクトが予測エラーを通知された時、同期バックエンドがより正確なステートまでゲームを巻き戻し、予測エラーを修正するためシングルステップ処理を進めます。
|
||||
|
||||
|
||||
## 入力キューオブジェクト(Input Queue Object)
|
||||
|
||||
入力キューオブジェクトはローカル、または遠隔プレイヤー用に受信した全入力を追跡します。所持していない入力を要求された場合、入力キューは次の入力を予測し、後の情報を追跡します。そうすることで同期オブジェクトは予測が誤った場合にどこまでロールバックすればよいのか分かります。リクエストがあった場合、入力キューはフレーム遅延も実行します。
|
||||
|
||||
|
||||
## UDPプロトコルオブジェクト(UDP Protocol Object)
|
||||
|
||||
UDPプロトコルオブジェクトは両プレイヤー間の同期と入力交換プロトコルを扱います。また、ゲーム入力の圧縮と信頼できるUDP層も実装しています。各UDPプロトコルオブジェクトにはTimeSyncオブジェクトが含まれ、プレイヤー間の時間のずれを推測するために利用しています。
|
||||
|
||||
|
||||
## UDPオブジェクト(UDP Object)
|
||||
|
||||
UDPオブジェクトは単純なUDPパケットの送受信を行います。他のプラットフォームへの移植を簡単にするため、UDPプロトコルから切り離されています。
|
||||
|
||||
|
||||
## 同期テストバックエンド(Sync Test Backend)
|
||||
|
||||
(図にはありません)同期テストバックエンドは、P2Pバックエンドがアプリのセーブステートと決定的に機能上実行していることを確認するときに同じ同期オブジェクトを使用します。同期テストの使用に関する詳しい情報は、開発者向けガイドを参照してください。
|
||||
|
||||
@@ -12,42 +12,42 @@ Rollback networking is designed to be integrated into a fully deterministic peer
|
||||
|
||||
### In Theory...
|
||||
|
||||
Take a look at the diagram below. It shows how 2 clients are kept synchronized in an ideal network with 0 milliseconds of latency. Player 1’s inputs and game state are shown in blue, player 2’s inputs are shown in red, and the network layer is shown in green. The black arrows indicate how inputs move through the system and transitions from one game state to the next. Each frame is separated by a horizontal, dashed line. Although the diagram only shows what happens from the perspective of player 1, the game on player 2’s end goes through the exact same steps.
|
||||
Take a look at the diagram below. It shows how 2 clients are kept synchronized in an ideal network with 0 milliseconds of latency. Player 1's inputs and game state are shown in blue, player 2's inputs are shown in red, and the network layer is shown in green. The black arrows indicate how inputs move through the system and transitions from one game state to the next. Each frame is separated by a horizontal, dashed line. Although the diagram only shows what happens from the perspective of player 1, the game on player 2's end goes through the exact same steps.
|
||||
|
||||

|
||||
|
||||
The inputs for player 1 are merged with the inputs from player 2 by the network layer before sending them to the game engine. The engine modifies the game state for the current frame using those inputs. Player 2 does the same thing: merging player 1’s inputs with his own before sending the combined inputs to the game engine. The game proceeds in this manner every frame, modifying the previous frame's game state by applying logic according to the value of the the player inputs. Since player 1 and player 2 both began with the same game state and the inputs they send to their respective engines are the same, the game states of the two players will remain synchronized on every frame.
|
||||
The inputs for player 1 are merged with the inputs from player 2 by the network layer before sending them to the game engine. The engine modifies the game state for the current frame using those inputs. Player 2 does the same thing: merging player 1's inputs with his own before sending the combined inputs to the game engine. The game proceeds in this manner every frame, modifying the previous frame's game state by applying logic according to the value of the the player inputs. Since player 1 and player 2 both began with the same game state and the inputs they send to their respective engines are the same, the game states of the two players will remain synchronized on every frame.
|
||||
|
||||
### In Practice..
|
||||
|
||||
The Ideal Network example assumes that packets are transmitted over the network instantaneously. Reality isn’t quite so rosy. Typical broadband connections take anywhere between 5 and 150 milliseconds to transmit a packet, depending on the distance between the players and the quality of the infrastructure where the players live. That could be anywhere between 1 and 9 frames if your game runs at 60 frames per seconds.
|
||||
The Ideal Network example assumes that packets are transmitted over the network instantaneously. Reality isn't quite so rosy. Typical broadband connections take anywhere between 5 and 150 milliseconds to transmit a packet, depending on the distance between the players and the quality of the infrastructure where the players live. That could be anywhere between 1 and 9 frames if your game runs at 60 frames per seconds.
|
||||
|
||||
Since the game cannot process the frame until it has received the inputs from both players, it must apply 1 to 9 frames of delay, or “lag”, on each player’s inputs. Let’s modify the previous diagram to take latency into account:
|
||||
Since the game cannot process the frame until it has received the inputs from both players, it must apply 1 to 9 frames of delay, or "lag", on each player's inputs. Let's modify the previous diagram to take latency into account:
|
||||
|
||||

|
||||
|
||||
In this example it takes 3 frames to transmit a packet. This means the remote inputs sent by player 2 at frame 1 don’t arrive at player 1’s game console until 3 frames later. The game engine for player 1 cannot advance until it receives the input, so it’s forced to delay the frame 1 for 3 frames. All subsequent frames are delayed by 3 frames as well. The network layer is generally forced to delay all merged inputs by the maximum one way transit time of the packets sent between the two players. This lag is enough to substantially affect the quality of the game play experience for many game types in all but the most ideal networking conditions.
|
||||
In this example it takes 3 frames to transmit a packet. This means the remote inputs sent by player 2 at frame 1 don't arrive at player 1's game console until 3 frames later. The game engine for player 1 cannot advance until it receives the input, so it's forced to delay the frame 1 for 3 frames. All subsequent frames are delayed by 3 frames as well. The network layer is generally forced to delay all merged inputs by the maximum one way transit time of the packets sent between the two players. This lag is enough to substantially affect the quality of the game play experience for many game types in all but the most ideal networking conditions.
|
||||
|
||||
## Removing Input Delay with Rollback Networking
|
||||
|
||||
### Speculative Execution
|
||||
|
||||
GGPO prevents the input lag by hiding the latency required to send a packet using speculative execution. Let’s see another diagram:
|
||||
GGPO prevents the input lag by hiding the latency required to send a packet using speculative execution. Let's see another diagram:
|
||||
|
||||

|
||||
|
||||
Instead of waiting for the input to arrive from the remote player, GGPO predicts what the other player is likely to do based on past inputs. It combines the predicted input with player 1’s local input and immediately passes the merged inputs to your game engine so it can proceed executing the next frame, even though you have not yet received the packet containing the inputs from the other player.
|
||||
If GGPO’s prediction were perfect, the user experience playing online would be identical to playing offline. Of course, no one can predict the future! GGPO will occasionally incorrectly predict player 2’s inputs. Take another look at the diagram above. What happens if GGPO sent the wrong inputs for player 2 at frame 1? The inputs for player 2 would be different on player 1’s game than in player 2’s. The two games will lose synchronization and the players will be left interacting with different versions of reality. The synchronization loss cannot possibly be discovered until frame 4 when player 1 receives the correct inputs for player 2, but by then it’s too late.
|
||||
This is why GGPO’s method is called “speculative execution”. What the current player sees at the current frame may be correct, but it may not be. When GGPO incorrectly predicts the inputs for the remote player, it needs to correct that error before proceeding on to the next frame. The next example explains how that happens.
|
||||
Instead of waiting for the input to arrive from the remote player, GGPO predicts what the other player is likely to do based on past inputs. It combines the predicted input with player 1's local input and immediately passes the merged inputs to your game engine so it can proceed executing the next frame, even though you have not yet received the packet containing the inputs from the other player.
|
||||
If GGPO's prediction were perfect, the user experience playing online would be identical to playing offline. Of course, no one can predict the future! GGPO will occasionally incorrectly predict player 2's inputs. Take another look at the diagram above. What happens if GGPO sent the wrong inputs for player 2 at frame 1? The inputs for player 2 would be different on player 1's game than in player 2's. The two games will lose synchronization and the players will be left interacting with different versions of reality. The synchronization loss cannot possibly be discovered until frame 4 when player 1 receives the correct inputs for player 2, but by then it's too late.
|
||||
This is why GGPO's method is called "speculative execution". What the current player sees at the current frame may be correct, but it may not be. When GGPO incorrectly predicts the inputs for the remote player, it needs to correct that error before proceeding on to the next frame. The next example explains how that happens.
|
||||
|
||||
### Correcting Speculative Execution Errors with Rollbacks
|
||||
|
||||
GGPO uses rollbacks to resynchronize the clients whenever it incorrectly predicts what the remote player will do. The term "rollback" refers to the process of rewinding state and predicting new outcomes based on new, more correct information about a player's input. In the previous section we wondered what would happen if the predicted frame for remote input 1 was incorrect. Let’s see how GGPO corrects the error:
|
||||
GGPO uses rollbacks to resynchronize the clients whenever it incorrectly predicts what the remote player will do. The term "rollback" refers to the process of rewinding state and predicting new outcomes based on new, more correct information about a player's input. In the previous section we wondered what would happen if the predicted frame for remote input 1 was incorrect. Let's see how GGPO corrects the error:
|
||||
|
||||

|
||||
|
||||
GGPO checks the quality of its prediction for previous frames every time it receives a remote input. As mentioned earlier, GGPO doesn’t receive the inputs for player 2’s first frame until player 1’s fourth. At frame 4, GGPO notices that the inputs received from the network do not match the predicted inputs sent earlier. To resynchronize the two games, GGPO needs to undo the damage caused by running the game with incorrect inputs for 3 frames. It does this by asking the game engine to go back in time to a frame before the erroneously speculated inputs were sent (i.e. to "rollback" to a previous state). Once the previous state has been restored, GGPO asks the engine to move forward one frame at a time with the corrected input stream. These frames are shown in light blue. Your game engine should advance through these frames as quickly as possible with no visible effect to the user. For example, your video renderer should not draw these frames to the screen. Your audio renderer should ideally continue to generate audio, but it should not be rendered until after the rollback, at which point samples should start playing n frames in, where n is the current frame minus the frame where the sample was generated.
|
||||
Once your engine reaches the frame it was on before GGPO discovered the error, GGPO drops out of rollback mode and allows the game to proceed as normal. Frames 5 and 6 in the diagram show what happens when GGPO predicts correctly. Since the game state is correct, there’s no reason to rollback.
|
||||
GGPO checks the quality of its prediction for previous frames every time it receives a remote input. As mentioned earlier, GGPO doesn't receive the inputs for player 2's first frame until player 1's fourth. At frame 4, GGPO notices that the inputs received from the network do not match the predicted inputs sent earlier. To resynchronize the two games, GGPO needs to undo the damage caused by running the game with incorrect inputs for 3 frames. It does this by asking the game engine to go back in time to a frame before the erroneously speculated inputs were sent (i.e. to "rollback" to a previous state). Once the previous state has been restored, GGPO asks the engine to move forward one frame at a time with the corrected input stream. These frames are shown in light blue. Your game engine should advance through these frames as quickly as possible with no visible effect to the user. For example, your video renderer should not draw these frames to the screen. Your audio renderer should ideally continue to generate audio, but it should not be rendered until after the rollback, at which point samples should start playing n frames in, where n is the current frame minus the frame where the sample was generated.
|
||||
Once your engine reaches the frame it was on before GGPO discovered the error, GGPO drops out of rollback mode and allows the game to proceed as normal. Frames 5 and 6 in the diagram show what happens when GGPO predicts correctly. Since the game state is correct, there's no reason to rollback.
|
||||
|
||||
# Code Structure
|
||||
|
||||
@@ -81,8 +81,8 @@ The UDP protocol object handles the synchronization and input exchange protocols
|
||||
|
||||
## UDP Object
|
||||
|
||||
The UDP object is simply a dumb UDP packet sender/receiver. It’s divorced from UDP protocol to ease ports to other platforms.
|
||||
The UDP object is simply a dumb UDP packet sender/receiver. It's divorced from UDP protocol to ease ports to other platforms.
|
||||
|
||||
## Sync Test Backend
|
||||
|
||||
(not pictured) The Sync Test backend uses the same Sync object as the P2P backend to verify your application’s save state and stepping functionality execute deterministically. For more information on sync test uses, consult the Developer Guide.
|
||||
(not pictured) The Sync Test backend uses the same Sync object as the P2P backend to verify your application's save state and stepping functionality execute deterministically. For more information on sync test uses, consult the Developer Guide.
|
||||
|
||||
Reference in New Issue
Block a user