Quickpong-Twistedフレームワークに基づいたネットワークゲームの開発

ゲームPongのオンラインバージョンをquickpong.comドメインで開発し、立ち上げました。 ゲームでは(設計上)、マルチプレイヤーモードのみが実装されます。つまり、ゲームは人工知能ではなく、他の人に対して行われます。



ゲームはクライアント/サーバーアプリケーションであり、サーバー部分はTwisted Pythonフレームワークで記述され、クライアント部分はFlashPunkフラッシュフレームワークで記述されています。



これは、数千の同時接続を処理できる非同期ネットワークアプリケーションを開発した最初の経験です。 次に、このプログラムがどのように機能するか、開発中にどのような問題に直面したか、どのアイデアを実装したいか、最終的には実現されなかったものについて説明します。 おそらく私の経験は誰かに役立つでしょう。



ゲームプレイについての2つの言葉



競技場では、左右に2つのボードがあり、垂直に移動できます。 各ボードは人によって管理されています。 ボード間でボールが動き、競技場とボードの壁を跳ね返ります。 プレーヤーのタスクは、ボールがボードの近くにある壁の外に落ちるのを防ぐことです。



使用技術の選択



新しい技術とツールを習得するとき、有能な詳細なドキュメントの可用性は大きな役割を果たします。 サーバーフレームワークを選択するとき、PythonのTwisted、Tornado、およびNode.jsを調べました。



そのようなアプリケーションを開発した経験がまったくなかったという事実に基づいて、Twistedを選択しました。 非常に詳細な入門コースが彼のために書かれており、フレームワークやプログラミング言語に関係なく非同期アプリケーションの開発の基本を説明し、もちろん、Twisted自体の使用についても説明しています。 非同期アプリケーションの開発の基本を理解したい人には、このコースを強くお勧めします。 上記のリンクを使用すると、このコースのロシア語への翻訳を見つけることができます。



このプロジェクトでは、サーバーサイドの開発が主な関心事でした。そのため、クライアント開発では、FlashPunkフレームワークの使用経験のあるフラッシュを使用せずに済ませることに決めました。



クライアント/サーバー相互作用アルゴリズム



クライアントとサーバー間の相互作用のアルゴリズムの選択については、フレームワークの選択ほど思慮深くアプローチしませんでした。 その結果、私はアルゴリズムの作業バージョンを3回だけ実装し、アルゴリズムの最初の2つのバージョンを自分で発明し、2回失敗した後、インターネットで3番目のバージョンを見つけました。



このようなアプリケーションの主な問題は、クライアントの同期です; 1人のクライアントに一度に1つの画像を表示させ、別のクライアントに別のクライアントを表示させることはできません。



さらに、クライアント側の詐欺を回避したいと思います。必要に応じて、スキルがあれば、クライアントswfを変更して、不正データをサーバーに送信できます。



したがって、アルゴリズムの最初のバージョンでは、次のように行動することにしました。

  1. 各クライアント自身がゲーム世界の状態(ボール、ボード、および相手のボードの位置)を計算します。
  2. 各クライアントは毎秒20回、サーバーのボードの位置の変化に関する情報と、ゲーム世界のボールのパラメーター(座標、ベクトル、速度)に関する情報を送信します。
  3. サーバーは各プレイヤーに対戦相手のボードの位置に関する情報を送信し、
  4. サーバー自体がゲームワールドの現在の状態を計算し、それをクライアントから受信したデータと比較します(ポイント2)。 クライアントとサーバーによって計算された状態の間に非同期がある場合、サーバーはクライアントを強制的に正しいと見なす状態に戻します。
アルゴリズムは動作しないことが判明しました。 彼は、データ交換の3人の参加者全員が同じ状態を持っている場合にのみ作業できます。 実際には、100%のケースで非同期が発生し、再生することは不可能でした。



アルゴリズムの2番目のバージョンでは、サーバー上のゲームワールドの状態を計算することから、リンクの1つを取り除くことにしました。

  1. 前と同様に各クライアントはボールとボードの座標を計算し、その状態に関するデータをサーバーに送信しました。
  2. サーバーはクライアントの状態を比較し、それらが異なる場合、クライアントの1つを強制的に2番目の状態に戻しました。
また、このアプローチは機能しませんでした。 2台の同じマシンで実行しているクライアントでさえ、ゲームワールドの状態を少し異なる方法で計算し、以前のケースのように3ではなく2つの状態を比較したにもかかわらず、いずれかのクライアントの状態が常に強制的に変更されたように見えましたボールとプレーの顕著な「ジャンプ」は不可能でした。



ゲームの世界の状態は、サーバー上の1か所でのみ計算する必要があることが明らかになりました。次に、実験を続ける前に、このような時間の無駄を避けるために、他の開発者の理論と経験を研究する必要があることが明らかになりました。



グーグル、私はこのような興味深いリンクをここに見つけました:



最後のリンクで説明したアルゴリズムは、このプロジェクトのために私が取ったものです。 その本質は次のとおりです。

  1. すべての計算はサーバー上でのみ実行されます。 クライアントは状態の変化をデルタサーバーに送信します。私の場合、これは以前のデータ転送に対するボードの位置の変化です。
  2. サーバーは、ボード(クライアント側の不正の可能性を抑制する)とボールの位置を計算し、生成されたデータフレームをクライアントに転送します。
  3. 私の場合、クライアントは受信したデータフレームを3フレームの遅延でレンダリングします。 これは重要なポイントです。 サーバーからクライアントへのデータの到着は不均一です。たとえば、ネットワークの問題により、クライアントは設定された50ミリ秒後ではなく、60〜70ミリ秒後に新しいボール位置に関するデータを受信できます。 つまり、最後のデータフレームが既にレンダリングされていて、新しいデータフレームがまだ到着していない場合があります。 この状況では、クライアントはボールを引くためのデータを持っていません。私の場合、ボールはデータの新しい部分が受信されるのを待つのを単に停止します。 このような状況を防ぐために、クライアントは3データフレームのデータを遅く描画します。これにより、サーバーからのデータが時間どおりに受信されない場合でも、クライアントがレンダリングするものがあります。 このアルゴリズムの不快な点は、ユーザーの操作(キーボードのボタンを押す)と画面上の変更の表示との間の顕著な遅延です。 この遅延を解消する方法はありますが、特にこの場合、クライアントを複雑にしないことにしました。




実装



サーバーとクライアントのソースをGithubに投稿しました。



この図: quickpong.com/images/quickpong.pngは、クライアントとサーバーの対話のロジックを示しています。



サーバー実装の観点から見ると、すべてが非常に透過的です。 ポート10080でイベントループが開始され、サーバーファクトリーQuickpongServerFactoryクラスが実行されます。 ファクトリーが初期化されると、 Quickpongクラスのインスタンスが作成されます。これには、サーバーとクライアントの対話のためのすべてのロジックが含まれます。



新しいクライアントが接続すると、ファクトリはbuildProtocolメソッドを呼び出し、参加した各クライアントに対してQuickpongProtocolクラスのインスタンスを作成し、作成されたオブジェクトはQuickpongに転送されます。 したがって、Quickpongクラスオブジェクトは、参加しているすべてのクライアントにアクセスでき、それらと必要な作業を実行できます。それらをペアリングしたり、ゲームワールドの状態を計算したりします。



QuickpongProtocolクラスのオブジェクトには、クライアントとの間でデータを送受信するためのメソッドのみが含まれています。



クライアントの実装では、すべてがシンプルであり、唯一の興味深い点は次のことでした。 FlashPunkを使用して、ピクチャリフレッシュレート(FPS)を設定できますが、FlashPunkは1秒あたりNフレームを描画することを保証できますが、すべてのフレームが1 / N秒で描画されることを保証できません。 つまり、FPS 50では、理想的な場合、各フレームを20ミリ秒で描画する必要があります。実際の場合、1つのフレームを15ミリ秒で描画し、もう1つのフレームを25ミリ秒で描画できます。 ボールが一定の速度(1秒あたり10ピクセルなど)で移動し、各データフレームのレンダリングがFlashPunkフレームのレンダリングと一致する場合、1つのケースでは15ミリ秒で10ピクセル、別の場合では10 25。



この機能はクライアントで考慮する必要があり 、フレームをレンダリングする前に、前のフレームが描画されてから経過した時間を確認します。これに基づいて、データフレームを完全にレンダリングするか、部分的にレンダリングするかを決定します。



テストと監視



私にとって最も興味深い質問は、このサーバーをサポートできるオンラインプレーヤーの数です。 テストのために、人間の行動をエミュレートする小さなクライアントをpythonで作成しました。



テストは、1つのIntel®Xeon®CPU E31275 @ 3.40GHzコアが割り当てられた仮想マシンで実施されました。



同じTwistedを使用して、ポート10082でWebサーバーを切断しました。このサーバーには、コンマ区切りのオンラインユーザー数とアクティブなゲーム数が表示されます。 この情報に基づいて、Pythonライブラリpsutilrrdtool + py-rrdtoolバンドルを使用して、現在のオンラインユーザー数と消費されたリソースに関する情報を消化可能な形式で表示するスクリプト作成しました: quickpong.com/stats.html (写真は分)。



5,000(5,000)人のプレーヤーで、プログラムは約100 MBのRAMを消費し、CPUを平均で30〜40%読み込みます。



ロードマップ



まだ実現されていないアイデア:



現時点では、私はこのプロジェクトの開発に興味を失いました。多分私の業績は有益な情報や興味深い情報に思えるかもしれません。このため、すべてのソースをGithubに投稿しました。




All Articles