ネットワークゲームで堅牢な方法でエンティティIDを割り当てるにはどうすればよいですか?


17

ネットワークゲームのエンティティシステムに取り組んでおり、各エンティティに一意の32ビット整数IDを割り当てています。このIDを使用して、エンティティおよびエンティティ自体への参照をシリアル化できます。

現在、エンティティが作成されるたびにカウンターをインクリメントしています。IDが最終的に使い果たされると思いますが、40億のエンティティがあるとは本当に思っていません。また、これにより、エンティティ#5が破棄され、IDが5になった場合に問題が回避されます。新しい#5を参照するのか、古い#5を参照するのですか?

問題は、衝突の処理/回避方法がわからないことです。現在、クライアントが現在の「フリーID」よりも高いIDを持つエンティティの更新を受け取ると、それまでのフリーIDにぶつかります。しかし、それはあまり堅牢ではないようです。

競合することなくエンティティを割り当てることができるように各クライアントに範囲を割り当てることを考えました(上位nビットはプレーヤー番号です)が、範囲が時間とともに重複し始めた場合にどうなるか心配です。

これを処理するより良い方法はありますか?idがオーバーフローしたり、許可された範囲の終わりを超えたりすることさえ気にする必要がありますか?これらのケースを検出するコードを追加できますが、クラッシュ以外の事態が発生した場合はどうなりますか。

もう1つのオプションは、128ビットGUIDのように一意である可能性が高いものを使用することですが、ネットワークトラフィックを最小限に抑えようとしているゲームにとっては非常に重いようです。また、現実的には、32ビットまたは24ビットの整数に収まるエンティティが一度に必要になることはありません。

ありがとう!


1
すべてのクライアントが同じエンティティを持たないのはなぜですか?クライアントは同期されていませんか?または、クライアントがすべて同じゲームを実行するわけではない、ある種の大きな世界です。
フィリップ

2
これまでの私のアーキテクチャは、UE3のアーキテクチャにほぼ準拠しています(詳細はこちら)。基本的に、クライアントは世界の近くにあるエンティティについてのみ知っています。また、クライアントはロックステップで実行されませんが、サーバーはほとんどのロジックを制御し、いつでもクライアントデータを上書きできます。私はそれについて考えると、サーバーがエンティティを作成し、クライアントがこれを行うためにRPCを使用することのみを許可することができたと思います。最善のアプローチがわかりません。私は日ごとにグラフィックプログラマーです:)
ルーカス

1
あなたが言うように、それがあなたの与えられたアーキテクチャ内で実行可能である場合、それはサーバーによってのみ処理されるべきだと思います。次に、存在する無料のエンティティIDのスタックをエンティティリスト/マップとは別に保持して、使用可能なIDを把握します。信頼できるサーバーモデルに失敗した場合、範囲の観点からは、範囲アプローチは問題なく機能するはずです。MMOで4000人のプレーヤーに分けても、40億はたくさんあります。次に、同じ方法を使用して、認証と同様に使用可能なIDを追跡します。サーバ。
エンジニア

@Lucas、リンクは「サーバーは各クライアントの「関連する」アクターのセットを識別します」と言っています。これは、サーバーがすべてのエンティティを認識しており、それらを列挙できる状態にあることを意味します。
キロタン

1
確かに、クライアントが新しいエンティティAを作成し、作成メッセージを取得する前にサーバーが新しいエンティティBを作成すると、両方に同じ「空き」IDが割り当てられます。
ルーカス

回答:


13

私がやったことは、サーバーにすべてをさせることです。クライアントは単にサーバーに何かをするように要求できますが、自分で何もすることはできません。この場合、サーバーは常にIDを割り当て、問題を解決します。

サーバーが「ロケットを撃つ」や「ここにソーラーステーションを作る」などのアクションを承認するのを待つ間、クライアント側の予測に対処していません。これらのアクションはエンティティを作成する必要があり、エンティティにはIDがあります。これまでのところ、私は親指でサーバーを待っていますが、サーバーの承認を待つ間に一時的なエンティティを作成する必要があると考えています。サーバーの承認を受け取ると、サーバーはIDを割り当て、一時オブジェクトを更新または上書きできます。

IDのオーバーフローも処理していませんが、サーバーがフルコントロールでオーバーフローを検出した場合、必要と思われる処理(0から再開、空きスタックから選択、クラッシュなど)を実行できます。クライアントは知りませんし、気にしません。クライアントは、サーバーから渡されたIDを受け入れるだけです。


良い情報を提供してくれたすべての人に感謝します!サーバーですべてのエンティティを作成するというアプローチに行き着きましたが、遅延が長すぎる場合は、Trevorの方法を試してみます。
ルーカス

クライアント固有のID(サーバーの待機中に予測が必要)の場合は、単にIDにプレフィックスを使用できます。
danijar

6

商用のマルチプレイヤーゲームでこれを行ったとき、私はあなたが提案したとおりに行いました。32ビットGUID整数を使用します。上位8ビットはプレーヤー番号で、下位24ビットにはローカルで一意の番号が含まれます。

ローカル番号がオーバーフローした場合(私の場合、ほとんど発生しません。通常の使用では、単一のネットワークセッションで4〜5日間連続して再生すると、発生します)、所有者は「すべてのオブジェクトのリセット」メッセージ、およびゼロから始まるすべてのまだ存在するオブジェクトの番号を変更します。メッセージはすべてのピアに、受け取ったオブジェクトを破棄し、再度照会するように指示しました。

より洗練されたアプローチは、既存のすべてのオブジェクトに対して「GUID 'n'のオブジェクトはGUID 'm'のオブジェクトになりました」というメッセージです。しかし、私の場合、実際に起こることはまずなく、1回のネットワークセッションで5日間ノンストップでプレイした後、世界中の人々が0.5秒間消えるリモートオブジェクトを本当に気にかけるとは思いませんでした。;)


これはオーバーフローを処理するための良いアイデアです。シンプルですが、私はそれを考えていませんでした:)。すべてのエンティティを「忘れる」のは、クライアントがゲームに参加するときに使用するコードパスと同じコードパスを再利用できるためです。
ルーカス

4

クライアントが独自のエンティティを生成できる場合、ピアツーピアのマルチプレイヤーゲームがあると思います。

その場合は、おそらくクライアントが多すぎません。確かに256以下です。また、エンティティIDは24ビットに収まることが保証されています(16000000以上のエンティティで十分です!)。したがって、IDの最上位バイトをクライアントのIDと等しくするだけです。

entityId = clientId<<24 + (maxEntityIn++)

か何か。

そして、私が間違っていて、権限のあるサーバーがある場合は、クライアント上に新しいエンティティを作成しないでください。


1

私は永続的なマルチプレイヤーゲームで「最も素朴な」方法(新しいIDごとに整数を増やす)を使用していますが、クライアントに新しいIDを作成させないため、うまく機能します。

クライアントに(説明されたGUID手法を使用して)決定させると、クライアントは古いIDを新しいアイテムに割り当てることでさまざまなバグを導入することもできます(これは5秒のように頭の中で考えたことです) 、他の抜け穴がたくさんあるかもしれません)。

いつものように、不正行為を防ぐために、サーバーはすべての作成と検証を行う必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.