Was sind die besten Möglichkeiten, um Netzwerknachrichten für C / C ++ - Multiplayer-Spiele zu serialisieren und unserialisieren?

11

Wir verwenden derzeit JSON und möchten für einige Arten von Nachrichten zwischen Client und Server in ein Binärformat wechseln.

Soll ich nur Strukturen in den Socket lesen? Proticol-Puffer / Sparsamkeit verwenden?

Wie soll ich Datenfelder darstellen?

Wie soll die Schnittstelle zum Packen / Entpacken von Daten aussehen?

HaltingState
quelle

Antworten:

12

Vorausgesetzt ...

  1. Sie sprechen über die Konvertierung in einen Puffer von Bytes
  2. Sie verwenden UDP und die Leistung ist ein Problem

Vermeiden Sie Platzverschwendung in Ihrem Paket für die Definition der Struktur. IE sendet mindestens ein Byte, um den Pakettyp zu bezeichnen, und nimmt dann einfach an, dass jedes empfangene Paket der vordefinierten Struktur für diesen Pakettyp folgt

Soll ich nur Strukturen in den Socket lesen? Proticol-Puffer / Sparsamkeit verwenden?

  • Ja, lesen Sie die gesamte Struktur, wenn Sie die gesamte Struktur benötigen
  • Nein, machen Sie die Paketstruktur selbst. Dies ist sicherlich kleiner als die Serialisierung mit diesen Methoden. Sie sollten genau wissen, welche Daten das Paket enthalten soll

Wie soll ich Datenfelder darstellen?

  • Als Datenfelder. Lesen Sie beim Empfang den Puffer bis zum Ende der Daten weiter, um zu vermeiden, dass eine Anzahl der Elemente des Arrays gesendet wird

Wie soll die Schnittstelle zum Packen / Entpacken von Daten aussehen?

  • Sie können problemlos eine Reihe von Methoden einrichten, um Basistypen in Bytes zu konvertieren. Von dort aus können Sie auf diesen Methoden aufbauen, um auch benutzerdefinierte Typen zu konvertieren. Die Besonderheiten auf , wie dies tun könnte fast überall zu finden Ich bin sicher , (ich verwende C # persönlich)

Eine letzte Sache, die Paketgröße ist ein Problem, insbesondere für einen Schnappschuss: Größe = Paketgröße x Entitäten x verbundene Spieler; Sie haben also möglicherweise 60 x 10 x 16 = 9.600 Bytes pro Paket. Senden Sie diese dann 20 Mal pro Sekunde: = 192.000 Bit / s = 187 KBit / s. Dies ist offensichtlich eine hohe Upload-Geschwindigkeit mit Bandbreite. Daher muss jeder der Faktoren, die zur Paketgröße beitragen, nach Möglichkeit minimiert werden.

Dieser Artikel hat mir sehr geholfen: Valve Multiplayer Networking

in der Tat 005
quelle
Ein weiterer Artikel, den ich vor einigen Wochen beim Lesen verschiedener Fragen zur Objektserialisierung und zum Netzwerk entdeckt habe, war dieser, der beschreibt, wie die Unreal-Engine dies tut. Ein guter Vergleichspunkt für die Ventilquelle.
Martin Foot
1
Ihre Array-Methode wird im allgemeinen Fall nicht funktionieren - wo ist das "Ende der Daten"? Selbst wenn Ihre Nachrichten begrenzt sind, bedeutet dies, dass Sie nicht mehr als 1 Array pro Struktur haben können. Um dies zu beheben, kann das Originalposter entweder an Arrays mit fester Länge festhalten oder sicherstellen, dass nur 1 Array pro Struktur (am Ende der Struktur) vorhanden ist, oder am Anfang des Arrays einen Zählwert senden.
Kylotan
Nur noch ein Tipp: Denken Sie daran, Endianess zu behandeln. Dies kann sehr ärgerlich sein, wenn Sie nicht wissen, dass es so etwas gibt.
Guter Punkt @Kylotan, stimme zu, dass diese zusätzlichen Daten in bestimmten Fällen nicht vermieden werden können; Wenn ich jedoch mehrere Arrays zu einem einzelnen Paket hinzufüge, würde ich stattdessen mehrere Pakete senden
tatsächlich 005
1

Dieses Problem wurde von Google und Facebook gelöst:

  1. Googles Protokollpuffer - Google ist ein großer Nutzer von C ++:

    Protokollpuffer sind eine Möglichkeit, strukturierte Daten in einem effizienten und dennoch erweiterbaren Format zu codieren. Google verwendet Protokollpuffer für fast alle internen RPC-Protokolle und Dateiformate.

  2. Apache Thrift (früher von Facebook):

    Thrift ist ein Software-Framework für die skalierbare Entwicklung sprachübergreifender Dienste. Es kombiniert einen Software-Stack mit einer Code-Generierungs-Engine, um Dienste zu erstellen, die effizient und nahtlos zwischen C ++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C #, Kakao, JavaScript, Node.js, Smalltalk und OCaml arbeiten.

ein bezahlter Nerd
quelle
Die Protokollpuffer von Google sind zu langsam für größere Echtzeitspiele. Aufgrund der Versionierung fand ich sie jedoch sehr gut für Prototypen und kleinere Spielernummern. Wie üblich wird Ihr Profiler die wahre Geschichte erzählen.
Patrick Hughes
Nun, sie sind gut genug für Google und Google skaliert ziemlich gut und sie haben gut funktioniert, als ich sie verwendet habe. Deshalb habe ich sie empfohlen.
Ein bezahlter Nerd
Google benötigt keine Echtzeitleistung. Google benötigt Zuverlässigkeit und Verfügbarkeit, die beide von Protokollpuffern gut bedient werden. Die Komplexität all dieser Fallback-Versionierung und der Generierung von Boilerplate-Code erhöht den Overhead. Wenn Sie 1000 Updates in Intervallen von 50 bis 100 ms senden und empfangen, summiert sich dies. Profilieren Sie einen Protokollpuffer mit mehreren alten Versionen gegen einen codierten Serializer, der für die vorliegenden Daten spezifisch ist. @ Indeed005 hat den Kern davon.
Patrick Hughes
+1, denn obwohl diese Formate für die meisten Echtzeitspiele oder Spiele mit hoher Bandbreite etwas zu groß und zu langsam sind (da sie zusätzliche Informationen enthalten, mit denen Sie beliebig komplexe Pakete rekonstruieren können), heißt das nicht, dass sie in nicht nützlich sind einige Spiele, z. rundenbasierte. Wenn nicht jede Ressource optimiert werden muss, können Sie mit diesen Formaten viel Zeit sparen und sind mit Sicherheit effizienter als JSON.
Kylotan