オンラインマルチプレイヤーゲームでのサーバー側の実装

私の短い人生の間、私はゲームが好きではないプログラマーに会ったことがありません。 そしてさらにそう、それらを書いたことのないプログラマー。
誰かは、ヘビのいるテトリスから始まります。 一部の人にとって、この趣味は過ぎ去りますが、誰かがこれに「病気」になり、自分の病気をお気に入りの作品や面白い趣味に変えます。

インターネットやソーシャルネットワークの時代に遊ぶことに興味はなく、友人とコミュニケーションを取り、遊びたいと思っています。
そして、ただ話すだけでなく、ダンジョンのグループに参加したり、アリーナのボスを見せたりします。
この記事では、このような対話のサーバー実装に対する私のアプローチについてお話したいと思います。



挑戦する


プレイヤーがアイテムを売買する、アリーナで戦う、単独でまたは友人とダンジョンに行くことができるキャラクターを制御する単純なゲームアプリケーションの例を考えてみましょう。

コンセプト


たとえば、プレイヤーはダンジョンに行くためのグループを作成します。 プレーヤーはサーバーにリクエストを送信し、旅行に利用可能なグループのリストに新しいグループを追加します。その後、他のプレーヤーがこのリストを取得して参加する機会があります。 グループはいっぱいになり、ボタンをクリックするだけで、作成者はそれをダンジョンに送り、危険なモンスターに会います。

すべてがシンプルに思えます。 データベースにテーブルを作成し、必要な人を書き留めてダンジョンに送ります。
多くの初心者プログラマーはそう考えていますが、マルチタスクシステムの最も重要な問題である競合状態を忘れています

この相互作用の正しい実装は、ゲームワールドの状態を担当し、プレーヤーからのリクエストを(優先度順に)受信し、処理して結果を返すサーバーデーモンになると想定するのは論理的です。
この場合、結果を正しく処理できるようにするために、サーバーはプレーヤーの現在の状態を保存する必要があります。

概略的には、次のようになります。

私たちの悪魔の基本的なスキーム

おそらく最も興味深いのはリクエストハンドラです。

サーバーの基本的なロジックが含まれています。
-プレイヤーのステータスの保存(無料、ダンジョン内、グループ内など)
-ゲームの世界に関する情報の保存と処理(アリーナバトル、ダンジョントリップ、キャンペーングループ)

たとえば、キャラクターがダンジョンでモンスターのジェノサイドに従事している間は、アリーナに行ったり、新しいアーマーを購入したりすることはできません。

さらに、ゲームワールドはサーバー上で処理する必要があります。
ダンジョンに5分間行ったとしましょう。 5分以内に、サーバーはモンスターとの戦闘を処理し、経験値を計算し、ドロップし、プレイヤーの状態を変更する必要があります。

プレーヤーの数が非常に多くなる可能性があり、サーバーが苦労することを忘れないでください。

実装



このようなデーモンを実装するために、libevent拡張機能を備えたPHP 5.3が作成者にとって最も馴染みのあるものとして選択されました。
libeventについては多くの記事があり、悪名高いphpDaemonが作成されているため、その作業を詳しく調べることは意味がありません。
保留中のイベント(フラグEV_TIMEOUT)を作成する可能性に注目する価値があるだけです。この場合、多くの問題が解決します。

ただし、サーバーは、とりわけ、データベースと連携して結果を記録したり、購入したりするのに十分なほどアクティブでなければなりません。
ご存知のように、データベースは深刻なアプリケーションのボトルネックであり、サーバーは多数のリクエストから「横になって」しまう可能性があります。

したがって、「重い」リクエストを処理するために、必要な数のスレッド(スレッド/ワーカー)を備えた追加のサーバーデーモンを提供できます。これにより、すべての骨の折れる作業を実行できます。

追加の悪魔

作業デーモンへの要求もキューに入れられ、応答が得られるまで処理されないことに注意してください。
作業デーモンの原理は、要求を処理するための複数のスレッド(スレッド/ワーカー)が存在することを除いて、メインデーモンと同じです。

結論



このアプローチの利点は次のとおりです。
-作業デーモンにすべてのハードワークをダンプし、メインデーモンにプレーヤー状態の処理を残します。これにより、非常に軽量で高速になります。
-作業デーモンは、必要に応じて別のサーバーに移動でき、ワーカーの数はハードウェアによって異なる場合があります
-スケーラビリティ

そして今、欠点について。
最大の実装上の欠陥:PHPが流れています。 5.3の新しいガベージコレクターでさえ、すべての問題を解決するわけではありません。 そして、私たちは持っています:

各ワーカーのメモリステータス

解決策:使用されるメモリの量が特定の制限に達すると、定期的にワーカーに過負荷をかけます。

私が見る最も正しい解決策は、プログラミング言語Erlang \ OTPの使用です。
提起されたタスクは、著者が近い将来行う予定のFSM / gen_serverの概念に完全に適合しています。

この記事が、初心者のゲーム開発者がレーキを踏んでアプリケーションを正しく設計しないようにするのに役立つことを願っています。

PSこの種のテーマがHabrの住民にとって興味深いものである場合、そのようなシステムの各コンポーネントについて詳しく説明する準備ができています。

Source: https://habr.com/ru/post/J122840/


All Articles