Clientsynchronisierung mit geringem Datenverkehr mit dem Server in MMO

22

Ich implementiere ein MMO, bei dem der Spieler auf seinem Raumschiff im Raum fliegt, es mit den Pfeiltasten steuert und mit anderen Spielern zusammenarbeitet.

Ich möchte es implementieren, damit Spieler in der Lage sind, ihrem Schiff vor einer Rakete oder etwas anderem auszuweichen. Deshalb versuche ich, den gesamten Spielstatus auf der Clientseite mithilfe des gleichen Algorithmus zur Weltsimulation wie bei der Servernutzung vorherzusagen. Diese Spielewelt ist in C # geschrieben und wird direkt im Client (in Unity3D) und über CLR auf einem C ++ - Server (unter Linux) aufgerufen. Verbindung über UDP.

Problem ist, wie man zum Beispiel 1000 Spieler innerhalb einer einzelnen Karte verwaltet (ausgenommen alle anderen Spielobjekte, Mobs ...): Sagen wir, ich werde:

  • Synchronisieren Sie den Server mit den Clients 50-mal pro Sekunde
  • Sende jedem Kunden nur die Zustände der Spielobjekte (und Spieler), die er sehen kann (innerhalb eines bestimmten Radius)
  • muss jedem Spieler innerhalb seines Sichtradius 100 Objekte schicken
  • muss durchschnittlich 50 Bytes pro Spielobjekt senden (ID, x, y-Koordinaten, Rotation, Status ...)

Daher wird eine solche Netzwerkbandbreite benötigt: 1000 (Clients) * 50 (Zeiten pro Sekunde) * 100 (Objekte, die an jeden Spieler gesendet werden sollen) * 50 (Bytes pro Objekt) = 250 000 000 Bytes pro Sekunde! Es ist unmöglich!

Ist es möglich, diesen Wert irgendwie zu reduzieren? Lassen Sie die Clients beispielsweise ihre Spielwelten (über einen längeren Zeitraum) vollständig simulieren und ihnen nur Eingaben von anderen Clients senden und die Spielwelten synchronisieren, sagen wir, alle paar Sekunden, aber dies führt aufgrund von Float-Berechnungen zu merkwürdigen Desynchronisierungsproblemen .

Wie sind solche Spiele eigentlich programmiert? Vielen Dank.

Slawisch
quelle
1
Ich sende nur logische Informationen über Objekte (Position der Welt, aktueller Zustand (das ist ein Byte) und so weiter) - keine Grafiken.
Slawischer
1
@Slav: Schön! All diese Bitverschiebungen erinnern mich an meine ASM-Programmiertage.
Randolf Richardson
1
Warum nicht "heute Tage"? :) Wenn ich auf AS3, Java, Lua, C # schreibe und auf so schlechte Leistung stoße, vermisse ich C ++ und erinnere mich an ASM.
Slawischer
1
@Slav: Heheh, ich habe in letzter Zeit nicht viel ASM gemacht. Die meisten Dinge für mich sind heutzutage in Java und Perl (meistens mod_perl2), aber ich mag diese Sprachen auch sehr.
Randolf Richardson
2
@Slav, Sie haben geschrieben: "Wenn ich auf AS3, Java, Lua, C # schreibe und mit einer so schlechten Leistung konfrontiert bin, dass ich C ++ vermisse und mich an ASM erinnere". Sie sollten lernen, wie man Lua und C # richtig einsetzt. Vielleicht finden Sie die Leistung weniger miserabel. Auch über die (angeblich) schnellste Skriptsprache zu klagen , ist bestenfalls einzigartig ... Ist dies ein Spiel über die Echtzeitanalyse des menschlichen Genoms?
Raine

Antworten:

20

Sie benötigen nur etwa 30 Updates (oder sogar weniger, vielleicht 10 oder 20) pro Sekunde. interpolieren Sie die Positionen von beweglichen Objekten clientseitig. Im Allgemeinen sollten Sie Daten nur dann senden, wenn sie WIRKLICH benötigt werden. In WoW erhalten Sie möglicherweise mehr Updates von den Spielern, mit denen Sie in einer Gruppe sind, als von den Spielern, die sich am selben Ort befinden. Auch wenn ein anderer Spieler weit von Ihnen entfernt ist, erhalten Sie nicht so viele Aktualisierungen pro Sekunde über ihn.

Senden Sie dann nur einen vollständigen Schnappschuss an jeden Spieler, wenn er eine Verbindung herstellt. Danach senden Sie nur die Änderungen der Spielobjekte. Wenn keine Änderung aufgetreten ist, senden Sie sie nicht.

Dann nutzen Sie BitVectors oder wie auch immer Sie sie nennen, um die Menge nicht benötigter Daten zu reduzieren! Beispiel: Sie können auch versuchen, einen Gleitkommawert mit nur einem Byte zu schreiben (in einem Bereich von 0 bis 1 oder -1 bis 1), sodass Sie nur 256 oder 128 verschiedene Werte haben. Durch die Interpolation bemerkt der Spieler jedoch keine ruckartigen Bewegungen.

Ein Beispiel mit LidgrenLibrary zum Komprimieren von Daten finden Sie hier: http://code.google.com/p/lidgren-network-gen3/wiki/Optimization

Weiter: Versuchen Sie, den Sichtradius der Spieler während ihrer Bewegung zu verringern und nur wichtige Informationen in dieser Zeit zu übertragen. Wenn sie dann anhalten, vergrößern Sie ihren Sichtradius erneut. Sie können ein räumliches Hashing-System oder einen BSP-Baum verwenden, um den Aufwand für die Suche nach Objekten zu verringern, die sich "in Reichweite" befinden. Dies ist eine gute Lektüre für das Thema: http://en.wikipedia.org/wiki/Collision_detection

Komprimieren Sie die Daten auch SELBST. Nur SIE kennen die Datenstruktur und die zeitliche Kohärenz in den Daten (die ausgenutzt werden können und sollten). Ein allgemeiner Algorithmus wie Bzip2 oder Deflate sollte verwendet werden, aber nur als letzte Komprimierungsstufe!

Für nicht spielkritische Informationen können Sie auch zusätzliche P2P-Techniken verwenden. Beispiel: Ein Spieler spielt die "Hallo" -Animation ab. (Nur ein grafischer Effekt) Der Spieler sendet diese Informationen an den Server, der Server leitet sie jedoch nicht an die anderen Spieler weiter. Stattdessen wird dieser unkritische Effekt vom Spieler selbst an die anderen Clients in Reichweite gesendet.

EDIT (wegen des Kommentars):

Zusätzliche Methoden zum Verringern der durchschnittlichen Bitzahl pro Sekunde für jeden Spieler:

  1. Sie haben geschrieben, dass Sie "Objekt wurde nicht geändert" senden. Es gibt keinen Grund, dies zu tun. Wenn Sie wegen Paketverlusts besorgt sind (und Ihre Simulation aus diesem Grund nicht mehr synchron ist), beachten Sie Folgendes: Bei jedem festgelegten Zeitschritt (z. B. 100, 200, 300, 400 ...) wird der Simulationsstatus gehasht und an den Server gesendet . Der Server bestätigt oder sendet einen vollständigen Schnappschuss aller Daten zurück.

  2. Für Dinge wie Raketen oder sogar Spieler können Sie nicht nur interpolieren, sondern auch extrapolieren, um die Simulation realistischer zu gestalten. Beispiel 'Rakete': Anstatt mit Nachrichten wie "Befindet sich jetzt an Position x" zu aktualisieren, senden Sie einfach eine Nachricht mit folgendem Inhalt: "Rakete gestartet: Position (Vektor), Uhrzeit (bei welchem ​​Simulationsschritt die Rakete gestartet wurde), Geschwindigkeit ( Vektor)". Sie müssen also nicht einmal die Drehung einbeziehen, da die Spitze immer in der "Geschwindigkeits" -Richtung ist.

  3. Kombinieren Sie mehrere Befehle in einer Nachricht und senden Sie niemals Nachrichten, die kleiner als 16-20 Byte sind, da der udp-Header größer als die Nachricht selbst ist. Senden Sie auch keine Pakete, die größer als die MTU Ihres Protokolls sind, da die Fragmentierung die Übertragungsgeschwindigkeit verlangsamt.

Riki
quelle
Oh, es ist eine gute Idee, einige Objekte häufiger als andere zu aktualisieren, P2P zu verwenden, die Gleitkommagenauigkeit zu verschlechtern, nur Änderungen zu senden (was für mich nicht trivial ist, da ich beabsichtige, Objekte periodisch zu synchronisieren, aber "Objekt hat sich nicht geändert" sind die Informationen auch). Mit all diesen Modifikationen sieht das ganze Bild realistischer aus!
Slawischer
1
Das Senden von Benachrichtigungen vom Typ "Objekt wurde nicht geändert" ist möglicherweise eine nützliche Technik für Testzwecke, bei denen Sie die Leistung Ihres Spiels überprüfen möchten, wenn die Spieler in Stoßzeiten aktiv sind, da dies die Verarbeitung und das Netzwerk möglicherweise stark beansprucht Es gibt noch bessere Lösungen als diese (z. B. das Erstellen eines eigenständigen Daemons, der einen tatsächlichen Charakter im Spiel steuert und diesen Deamon dann mehrmals von verschiedenen Computern aus ausführt).
Randolf Richardson
5

Hier sind zwei Ansätze:

Erstens:
Wechseln Sie zu deterministischer Physik, senden Sie Spielerbefehle, AI-Aktionen, Objekte, die sichtbar werden, und alles, was nicht clientseitig bestimmt werden kann, an Clients. Dies muss Nicht-Befehle beinhalten, eine Bestätigung, dass bis zu einem bestimmten Zeitpunkt nur die gesendeten und empfangenen Befehle gültig sind.

Der Client muss zwei oder drei gleichzeitige Simulationen ausführen.
1: Hält an, wenn Daten für den nächsten Schritt fehlen.
2: Verwenden Sie weiterhin Schätzdaten und geben Sie den für das Rendern verwendeten Status an. 3: Immer wenn Nr. 1 zum Stillstand kommt, kopiert diese Simulation den Zustand von Nr. 1, holt die aktuelle Zeit ein und übernimmt Nr. 2, die dann fallengelassen wird.

Wenn der Aufholprozess schnell genug ist, können Sie die Unterschiede zwischen Nr. 2 und Nr. 3 weglassen und die alten Daten sofort löschen.

Zweitens:
Verwenden Sie keine deterministische Physik, sondern senden Sie alle paar Sekunden "Vollbilder". Sie können das Übertragen von temporären Elementen wie Kugeln ganz einfach auslassen.

In beiden Fällen sollten Sie vorsichtig sein, wenn der Kunde vorhersagt, dass jemand stirbt. Es ist irgendwie albern, wenn ein Gegner explodiert.

Und +1 für das Rechnen, zu viele Leute machen keine einfachen Schätzungen des Ressourcenverbrauchs.

aaaaaaaaaaa
quelle
2
Bedeutet "deterministische Physik", dass ich keine Gleitkommawerte oder andere Simulationsschritte verwenden kann? Ich frage mich, ob eine kritische Desynchronisation stattfinden kann, wenn z. B. eine Rakete von einem feindlichen Turm auf dem Client abgefeuert wird, diese Rakete jedoch auf dem Server getroffen wird (aufgrund einer Gleitkomma-Ungenauigkeit), was dazu führt, dass der Spieler diesen Turm bis zum nächsten eingehenden Server-Synchronisationspaket weiter bekämpft (einige Sekunden).
Slawischer
3
Es bedeutet ganze Zahlen und festen Zeitschritt. Theoretisch könnten Sie Float-Punkte als Verhaltensmuster verwenden, aber die Verwendung von Ganzzahlen ist viel einfacher. Mit dem fehlenden Raketenbeispiel haben Sie Recht. Wenn Sie nicht-deterministische Physik verwenden, ist es wahrscheinlich am besten, den Server vollständig mit dem Tod fertig zu werden und Todesfälle / Zerstörungen schnell zu übermitteln.
aaaaaaaaaaa 18.06.11
5

Ein paar Fragen zuerst.

Sind die "Raketen oder etwas anderes" intelligent oder dumm? Wenn sie dumm sind, brauchen Sie nur den Zeitstempel des Feuers, den Ursprung und den Vektor, um ihren Weg zu simulieren. Wenn sie intelligent sind, wie intelligent sind sie? Können Sie zum Zeitpunkt des Feuers berechnen, ob sie treffen oder verfehlen werden? In diesem Fall können Sie den gesamten Pfad auf dem Client simulieren. ("Bei T13 wird die Rakete das Schiff treffen, weil das Spiel den Ausweichwurf verloren hat / der Schütze einen kritischen Treffer erzielt hat.")

Im Allgemeinen gibt es jedoch so gut wie keinen Grund: A) eine Taktrate von 50 Hz zu haben (die meisten Schützen kommen mit 15-20 und weniger MMOs davon). B) jeden Frame den vollen Status senden. (Kommt es überhaupt auf die Rotation einer Rakete im Weltraum an? Oder können Sie einfach davon ausgehen, dass sich ihre "Front" entlang des Vektors orientiert, den sie bewegt?)

Verbringen Sie Zeit mit Vorhersage und Interpolation, und Sie werden sehen, wie Ihre Bandbreite sinkt. Ein Projekt, an dem ich gearbeitet habe, hatte eine Aktualisierungsrate von 10 Hz und eine Objektzustandsdarstellung von 14 Byte. (Komprimiere alles, was du kannst! Ich glaube, wir haben 6 Bits verwendet, um die Rotation um die x-Ebene zu definieren, und dann weitere 6 Bits für eine Neigung über / unter dieser Ebene. Es sah nicht unterscheidbar aus, eine tatsächliche Rotationsmatrix / Quaternion zu senden.)

Sie können auch Objekte priorisieren. Zeigen Sie, vielleicht gibt es 100 Objekte in der relevanten Menge, aber kennen Sie sein Ansichtsstumpf auf dem Server? Wenn etwas nicht in seiner Sicht ist, können Sie die Aktualisierungshäufigkeit um eine Größenordnung verringern?

Die allgemeine Idee ist nicht, eine perfekte Simulation auf dem Client zu erstellen, das ist unmöglich. Die Idee ist, ein unterhaltsames Spiel zu erstellen, bei dem die Spieler nicht bemerken, dass es keine perfekte Simulation ist.

Doug-W
quelle