Im Mai dieses Jahres nahm ich als Spieler am
KatherineOfSky MMO-Event teil . Mir ist aufgefallen, dass alle paar Minuten, wenn die Anzahl der Spieler eine bestimmte Anzahl erreicht, einige von ihnen âabfallenâ. Zum GlĂŒck fĂŒr Sie (aber nicht fĂŒr mich) war ich einer der Spieler, die sich
jedes Mal getrennt
haben , auch wenn eine gute Verbindung bestand. Ich nahm dies als persönliche Herausforderung und begann nach den Ursachen des Problems zu suchen. Nach drei Wochen Debuggen, Testen und Beheben wurde der Fehler endgĂŒltig behoben, aber diese Reise war nicht so einfach.
Die Probleme von Multiplayer-Spielen sind sehr schwer zu finden. Normalerweise treten sie unter sehr spezifischen Bedingungen von Netzwerkparametern und unter sehr spezifischen Bedingungen des Spiels auf (in diesem Fall der Anwesenheit von mehr als 200 Spielern). Und selbst wenn es möglich ist, das Problem zu reproduzieren, kann es nicht ordnungsgemÀà getestet werden, da das EinfĂŒgen von Kontrollpunkten das Spiel stoppt, die Timer verwirrt und normalerweise zur Beendigung der Verbindung fĂŒhrt, weil die Wartezeit ĂŒberschritten wird. Aber dank der Sturheit und des wunderbaren Werkzeugs namens
ungeschickt gelang es mir herauszufinden, was los war.
Kurz gesagt: Aufgrund eines Fehlers und einer unvollstĂ€ndigen Implementierung der Simulation des Verzögerungsstatus befindet sich der Client manchmal in einer Situation, in der er ein Netzwerkpaket in einem Taktzyklus senden muss, das aus den Aktionen des Spielers zur Auswahl von etwa 400 Spieleinheiten besteht (wir nennen es ein âMegapackageâ). Danach sollte der Server alle diese Eingabeaktionen nicht nur korrekt empfangen, sondern auch an alle anderen Clients senden. Wenn Sie 200 Kunden haben, wird dies schnell zu einem Problem. Der Kanal zum Server ist schnell verstopft, was zu Paketverlust und einer Kaskade von erneut angeforderten Paketen fĂŒhrt. Das Verschieben von Eingabeaktionen fĂŒhrt dann dazu, dass noch mehr Kunden Megapackets senden und ihre Lawine noch stĂ€rker wird. Erfolgreiche Kunden können sich erholen, der Rest fĂ€llt ab.

Das Problem war ziemlich grundlegend und ich habe 2 Wochen gebraucht, um es zu beheben. Es ist ziemlich technisch, daher werde ich im Folgenden die saftigen technischen Details erlĂ€utern. Aber zuerst mĂŒssen Sie wissen, dass seit der Veröffentlichung der Version 0.17.54 am 4. Juni angesichts vorĂŒbergehender Verbindungsprobleme der Mehrspielermodus stabiler geworden ist und das Verbergen von Verzögerungen viel weniger fehlerhaft ist (weniger Bremsen und Teleportieren). AuĂerdem habe ich die Art und Weise geĂ€ndert, wie Verzögerungen im Kampf versteckt werden, und ich hoffe, dass sie dadurch etwas reibungsloser werden.
Mehrbenutzer-Megapack - Technische Details
In einfachen Worten funktioniert der Multiplayer im Spiel wie folgt: Alle Clients simulieren den Status des Spiels und empfangen und senden nur die Eingaben des Spielers (sogenannte âEingabeaktionenâ,
Eingabeaktionen ). Die Hauptaufgabe des Servers besteht darin,
Eingabeaktionen zu ĂŒbertragen und zu steuern, dass alle Clients dieselben Aktionen in einem Zyklus ausfĂŒhren. Lesen Sie mehr dazu in der Post
FFF-149 .
Da der Server Entscheidungen darĂŒber treffen muss, welche Aktionen ausgefĂŒhrt werden sollen, bewegen sich die Aktionen des Spielers auf diesem Pfad: Aktion des Spielers -> Spielclient -> Netzwerk -> Server -> Netzwerk -> Spielclient. Dies bedeutet, dass die Aktion jedes Spielers erst ausgefĂŒhrt wird, nachdem er einen Hin- und RĂŒckweg durch das Netzwerk gemacht hat. Aus diesem Grund scheint das Spiel furchtbar langsam zu sein, so dass fast unmittelbar nach dem Erscheinen des Multiplayers im Spiel ein Mechanismus zum Verbergen von Verzögerungen eingefĂŒhrt wurde. Das Ausblenden einer Verzögerung imitiert die Eingabe eines Spielers, ohne die Aktionen anderer Spieler und Serverentscheidungen zu berĂŒcksichtigen.

Factorio hat einen Spielstatus namens Spielstatus - dies ist der vollstÀndige Status der Karte, des Spielers, der EntitÀten und aller anderen Elemente. Es wird in allen Clients basierend auf den vom Server empfangenen Aktionen deterministisch simuliert. Der Spielstatus ist heilig, und wenn er sich jemals vom Server oder einem anderen Client unterscheidet, erfolgt eine Desynchronisierung.
ZusÀtzlich zum
Spielstatus haben wir einen
Latenzstatus . Es enthÀlt eine kleine Teilmenge des Grundzustands.
Der Latenzstatus ist nicht heilig und zeigt lediglich ein Bild davon, wie der Status des Spiels in Zukunft aussehen wird, basierend auf den vom Spieler eingefĂŒhrten
Eingabeaktionen .
Dazu speichern wir eine Kopie der erstellten
Eingabeaktionen in der Verzögerungswarteschlange.
Das heiĂt, am Ende des Prozesses auf der Client-Seite sieht das Bild ungefĂ€hr so ââaus:
- Wenden Sie die Eingabeaktionen aller Spieler auf den Spielstatus an, da diese Eingabeaktionen vom Server empfangen wurden.
- Wir entfernen alle Eingabeaktionen aus der Warteschlange der Verzögerungen, die laut Server bereits auf den Spielstatus angewendet wurden.
- Löschen Sie den Latenzstatus und setzen Sie ihn zurĂŒck, sodass er genauso aussieht wie der Spielstatus .
- Wenden Sie alle Aktionen aus der Verzögerungswarteschlange auf den Latenzstatus an .
- Basierend auf den Daten aus Spielstatus und Latenzstatus rendern wir das Spiel fĂŒr den Spieler.
All dies wiederholt sich in jedem Takt.
Zu kompliziert? Entspann dich nicht, das ist noch nicht alles. Um die unzuverlÀssigen Internetverbindungen auszugleichen, haben wir zwei Mechanismen geschaffen:
- Verpasste Ticks: Wenn der Server entscheidet, dass die Eingabeaktionen im Spieltakt ausgefĂŒhrt werden, wird er nicht warten, wenn er die Eingabeaktionen einiger Spieler nicht erhĂ€lt (z. B. aufgrund der erhöhten Verzögerung), sondern diesem Client mitteilen, dass ich dies nicht berĂŒcksichtigt habe Ich werde versuchen, Ihre Eingabeaktionen zur nĂ€chsten Kennzahl hinzuzufĂŒgen. " Dies geschieht, damit aufgrund von Problemen mit der Verbindung (oder mit dem Computer) eines Spielers die Kartenaktualisierung fĂŒr alle anderen nicht langsamer wird. Es ist erwĂ€hnenswert, dass Eingabeaktionen nicht ignoriert, sondern einfach verschoben werden.
- Roundtrip-Verzögerung: Der Server versucht zu erraten, wie hoch die Roundtrip-Verzögerung zwischen Client und Server fĂŒr jeden Client ist. Bei Bedarf bespricht er alle 5 Sekunden mit dem Client eine neue Verzögerung (abhĂ€ngig davon, wie sich die Verbindung in der Vergangenheit verhalten hat) und erhöht oder verringert dementsprechend die Verzögerung beim Hin- und HerĂŒbertragen von Daten.
Diese Mechanismen sind an sich recht einfach, aber wenn sie zusammen verwendet werden (was hÀufig bei Verbindungsproblemen auftritt), wird die Logik des Codes schwierig zu verwalten und es gibt eine Reihe von GrenzfÀllen. Wenn diese Mechanismen ins
Spiel kommen , mĂŒssen der Server und die Verzögerungswarteschlange auĂerdem eine spezielle
Eingabeaktion namens
StopMovementInTheNextTick korrekt implementieren. Aus diesem Grund lÀuft der Charakter bei Verbindungsproblemen nicht von alleine (z. B. unter dem Zug).
Jetzt mĂŒssen Sie Ihnen erklĂ€ren, wie die EntitĂ€tsauswahl funktioniert. Eine der Arten der ĂŒbergebenen
Eingabeaktion ist die Ănderung des Status der EntitĂ€tsauswahl. Es sagt jedem, ĂŒber welcher EntitĂ€t der Spieler schwebte. Wie Sie verstehen, ist dies eine der hĂ€ufigsten Eingabeaktionen, die von Clients gesendet werden. Um Bandbreite zu sparen, haben wir sie so optimiert, dass sie so wenig Platz wie möglich beansprucht. Dies wird wie folgt implementiert: Bei der Auswahl jeder EntitĂ€t behĂ€lt das Spiel anstelle der absoluten, hochprĂ€zisen Koordinaten der Karte einen relativen Versatz mit geringem Strom gegenĂŒber der vorherigen Auswahl bei. Dies funktioniert gut, da die Mausauswahl normalerweise sehr nahe an der vorherigen Auswahl erfolgt. Aus diesem Grund ergeben sich zwei wichtige Anforderungen:
Eingabeaktionen dĂŒrfen niemals ĂŒbersprungen und in der richtigen Reihenfolge ausgefĂŒhrt werden. Diese Anforderungen sind fĂŒr den
Spielstatus erfĂŒllt. Da es jedoch die Aufgabe des
Latenzzustands ist, fĂŒr den Spieler âgut genug auszusehenâ, sind sie im Zustand der Verzögerungen nicht zufrieden.
Der Latenzstatus berĂŒcksichtigt nicht
viele GrenzfĂ€lle, die mit Ăberspringzyklen und sich Ă€ndernden Umlaufverzögerungen verbunden sind.
Sie können bereits erraten, wohin alles geht. SchlieĂlich beginnen wir, die Ursachen des Megapackage-Problems zu erkennen. Die Wurzel des Problems besteht darin, dass bei der Entscheidung, ob die Aktion der AuswahlĂ€nderung bestanden werden soll, die EntitĂ€tsauswahllogik auf dem
Latenzstatus beruht und dieser Status nicht immer die richtigen Informationen enthÀlt. Daher wird ein Megapaket wie folgt generiert:
- Der Player hat ein Verbindungsproblem.
- Die Mechanismen zum Ăberspringen von Uhren und zum Regulieren der Umlaufverzögerung kommen ins Spiel.
- Verzögerungen im Warteschlangenstatus berĂŒcksichtigen diese Mechanismen nicht. Dies fĂŒhrt dazu, dass einige Aktionen vorzeitig gelöscht oder in der falschen Reihenfolge ausgefĂŒhrt werden, was zu einem falschen Latenzstatus fĂŒhrt .
- Der Spieler hat ein Problem mit der Verbindung und simuliert, um den Server einzuholen, bis zu 400 Taktzyklen.
- In jedem Taktzyklus wird eine neue Aktion generiert, die die Auswahl einer EntitÀt Àndert, und zum Senden an den Server vorbereitet.
- Der Client sendet ein Megapaket mit mehr als 400 Ănderungen bei der Auswahl der EntitĂ€ten an den Server (und bei anderen Aktionen: Der Zustand des SchieĂens, Gehens usw. litt ebenfalls unter diesem Problem).
- Der Server empfĂ€ngt 400 Eingabeaktionen. Da er keine einzelne Eingabeaktion ĂŒberspringen darf, weist er alle Clients an, diese Aktionen auszufĂŒhren, und sendet sie ĂŒber das Netzwerk.
Die Ironie ist, dass ein Mechanismus zum Einsparen von Kanalbandbreite infolgedessen riesige Netzwerkpakete erzeugt hat.
Wir haben dieses Problem gelöst, indem wir alle GrenzfĂ€lle der Aktualisierung und UnterstĂŒtzung der Warteschlange fĂŒr Verzögerungen korrigiert haben. Obwohl es eine Weile gedauert hat, hat es sich am Ende gelohnt, alles richtig zu implementieren und sich nicht auf schnelle Hacks zu verlassen.