Hilfe zum Austausch von Netzwerk-Client-Server-Nachrichten und zur Uhrzeitsynchronisierung

10

Ich mache ein schnelles Physikspiel, das ein Tischhockey ist. Mit zwei Schlägern und einem Puck. Das Spiel läuft auf dem iPhone / iPad und ich mache den Multiplayer-Teil über GameCenter.

So funktioniert das Netzwerksystem. Der Client, der die Übereinstimmung markiert, wird als Server abgerechnet, und derjenige, der die Übereinstimmungsanforderung akzeptiert, ist der Client.

Auf dem 'Server' läuft die Physik und die Antwort erfolgt sofort, und auf dem Client läuft auch die Physik, sodass sie zwischen dem Nachrichtenaustausch reibungslos aussieht. Was ich als Server mache, ist, dass ich dem Client meine Puckgeschwindigkeit und meine Position sende und der Client seine Puckgeschwindigkeit / Position in Bezug auf den Server anpasst, um ihn synchron zu halten. Andernfalls desynchronisiert die Physik und es vermasselt es.

Wenn die Netzwerklatenz gut ist, unter 100 ms sind die Ergebnisse ziemlich gut, ich habe ein reibungslos spielbares Spiel auf der Clientseite und das seltsame Verhalten ist minimal. Das Problem tritt auf, wenn die Verzögerung zwischen 150 und 200 ms liegt. In diesem Fall kommt es vor, dass mein Client-Puck bereits eine Kante und eine umgekehrte Richtung getroffen hat, aber eine Verzögerungsnachricht vom Server empfängt und sich etwas zurückzieht, was ein seltsames Gefühl für das Ballverhalten verursacht.

Ich habe ein paar Dinge darüber gelesen:

Beispiel für ein Pong-Netzwerk

Klicken Sie auf Synchronisierungsbeispiel

Wikipedia auf Clock Sync

Wie kann ich das lösen? Soweit ich gelesen habe, ist die beste Option, die ich habe, eine Uhrensynchronisation auf dem Server / Client mit einem Zeitstempel durchzuführen, so dass ich, wenn ich Verzögerungsmeldungen zu meiner Uhr erhalte, diese einfach ignoriere und die Client-Simulation das machen lasse Job. Seid ihr damit einverstanden? Und da ich unzuverlässige Daten (UDP) sende, kann es sein, dass ich verspätete oder nicht ordnungsgemäße Nachrichten erhalte.

Wenn das der beste Ansatz ist, wie implementiere ich die Uhrensynchronisation? Ich habe die Schritte gelesen, aber ich habe es nicht ganz verstanden.

Es steht dass:

  1. Der Client stempelt die aktuelle Ortszeit auf ein "Zeitanforderungs" -Paket und sendet sie an den Server.
  2. Nach dem Empfang beim Server stempelt der Server die Serverzeit und kehrt zurück
  3. Beim Empfang beim Client subtrahiert der Client die aktuelle Zeit von der gesendeten Zeit und dividiert durch zwei, um die Latenz zu berechnen. Es subtrahiert die aktuelle Zeit von der Serverzeit, um das Client-Server-Zeitdelta zu bestimmen, und addiert die halbe Latenz, um das richtige Taktdelta zu erhalten. (Bisher ist dieser Algothim SNTP sehr ähnlich)
  4. Der Client wiederholt die Schritte 1 bis 3 fünfmal oder öfter und hält jedes Mal einige Sekunden an. In der Zwischenzeit kann anderer Datenverkehr zulässig sein, sollte jedoch für beste Ergebnisse minimiert werden. Die Ergebnisse der Paketempfänge werden akkumuliert und in der Reihenfolge der niedrigsten Latenz bis zur höchsten Latenz sortiert. Die mittlere Latenz wird bestimmt, indem die Mittelpunktstichprobe aus dieser geordneten Liste ausgewählt wird.
  5. Alle Stichproben über ungefähr 1 Standardabweichung vom Median werden verworfen und die verbleibenden Stichproben werden unter Verwendung eines arithmetischen Mittels gemittelt.

Nach diesem Beispiel hätte ich folgendes:

Stellen wir uns vor, das Spiel wurde geladen und meine Client-Zeit ist jetzt 0, also sende ich an den Server, dass meine Zeit 0 ist.

Es dauert 150 ms, bis die Nachrichten zum Server gelangen, aber die Uhr des Servers wurde bereits gestartet und liegt 1 Sekunde vor dem Client. Wenn der Server die Nachricht erhält, lautet die Zeit: 1.15 und sendet diese Zeit an den Client. Sind wir gut? Stellen wir uns vor, unsere Verzögerung ist konstant bei 150 ms.

Jetzt empfängt der Client die Zeit 1.15 und subtrahiert die aktuelle Zeit von der gesendeten Zeit und dividiert durch zwei, um die Latenz zu berechnen. Was ist: 0,3 - 0 = 0,3 / 2 -> 150 ms.

Es subtrahiert die aktuelle Zeit von der Serverzeit, um das Client-Server-Zeitdelta zu bestimmen, und addiert die halbe Latenz, um das richtige Taktdelta zu erhalten:
Client-Zeit: 0,3 Serverzeit 1,15
0,3 - 1,15 = 0,85 + Latenz (0,15) = 1

Wie ist das synchronisiert? Was vermisse ich?

Es ist mein erstes Mal im Multiplayer- und Netzwerkerlebnis, daher bin ich etwas verwirrt.

Vielen Dank.

gmemario
quelle
Gute Frage. Könnten Sie korrigieren: 0,3 - 1,15 zu 1,15 - 0,3?
Ciaran

Antworten:

12

Der veröffentlichte Algorithmus war korrekt, aber in Ihrem Beispiel vergessen Sie die Zeit, die das Serverpaket benötigt, um zum Client zu gelangen.

Server time: 1
Client time: 0
Client sends 0 to server

... 150ms to get to server  (ping is 300! not 150ms in this case. Ping is round-trip)

Server time: 1.15
Client time: 0.15
Server receives packet and sends client 1.15

... 150ms to get back to client

Server time: 1.30
Client time: 0.30
Client receives 1.15 from server

Wie Sie sehen können, würde der Client, wenn er seine Uhr auf 1,15 ändern würde, 0,15 hinter dem Server liegen. Aus diesem Grund müssen Sie den Ping (auch bekannt als Round Trip Time [RTT]) anpassen. Hier ist die vollständige Deltazeitberechnung, die in vielen Schritten durchgeführt wird:

Server Time - Current Time + Ping / 2
= Server Time - Current Time + (Current Time - First Packet Time) / 2
= 1.15 (Perceived, not actual!) - 0.30 + (0.30 - 0.00) / 2
= 1.00

Dies gibt uns die korrekte Deltazeit von 1,00 Sekunden

John McDonald
quelle
Ich bekomme es, also ist meine Client-Uhr 1,00 und der Server 1,30. Nachdem ich eine Nachricht vom Server erhalten habe, muss ich den Ping hinzufügen, um zu überprüfen, ob es sich um eine verspätete Nachricht handelt oder so? Eine andere Sache, was ist, wenn sich die Latenz ändert, muss ich diese Berechnungen die ganze Zeit durchführen?
Gmemario
Die Deltazeit von 1,00 s muss zur aktuellen Uhr addiert werden, damit die Zeit des Clients der Zeit des Servers entspricht. Die Latenz ändert sich ständig, jedes Paket hat eine etwas andere RTT. In einem kurzen Zeitraum wie in einem Spiel würde ich diese Zeitsynchronisierung einfach nur einmal durchführen. Der Client hat eine ziemlich gute Vorstellung von der Zeit des Servers und beide Uhren sollten ungefähr im gleichen Tempo vorrücken. Die einzige Ausnahme wäre, wenn Sie ein Paket erhalten hätten, das anscheinend aus der Zukunft stammt. In diesem Fall gibt es zwei Lösungen: 1) Führen Sie eine neue Zeitsynchronisierung durch. 2) Stellen Sie Ihre Uhr auf die zukünftige Uhr ein
John McDonald
Ok, schauen wir uns diese Situation an, um sicherzugehen, dass ich sie habe: Serverzeit: 1s Clientzeit: 0s 150ms, um zum Server zu gelangen Serverzeit: 1,15s Clientzeit: 0,15s Server sendet Client 1,15s 200 ms, um zum Client zu gelangen Mein Ping beträgt jetzt 350 ms. Ist das korrekt? Serverzeit: 1,35 Clientzeit: 0,35 Berechnen: 1,15 - 0,35 + (0,35 - 0,00) / 2 Jetzt ist meine Deltazeit = 0,975 Wenn ich die Deltazeit 0,975 zu meiner 0,35 addiere, erhalte ich: 1,325 Was ist eine Art Desync. Ist das auch richtig?
gmemario
@ Gilson, das ist richtig. Wenn die Latenz für die beiden Pakete unterschiedlich ist (fast garantiert), ist die Deltazeitberechnung des Clients nicht perfekt. Es gibt keine Möglichkeit für den Kunden, jemals perfekt zu sein. In dem Algorithmus, den Sie in der Frage beschrieben haben, wurde die Deltazeit mehrmals berechnet, und es gab eine Methode zur Auswahl und Mittelung dieser Ergebnisse. Viele Zeitsynchronisierungsmethoden funktionieren in etwa so, sind jedoch normalerweise für geschäftskritische und lang laufende Server wie Börsen gedacht. Für die Zeitgenauigkeit, die Sie für ein kurzes Spiel benötigen, wäre das meiner Meinung nach übertrieben.
John McDonald
@Gilson, Solange der Client eine vernünftige Schätzung der Serverzeit hat und beide Uhren ungefähr gleich schnell vorwärts laufen, sollten Sie keine Probleme bemerken. Wenn der Client oder der Server eine Aktion an die andere Seite sendet, senden sie ihre Ortszeit. Auf der Empfangsseite sollte die Nachricht immer in der Vergangenheit liegen und muss als vergangenes Ereignis interpretiert werden. Dies bedeutet, dass Sie alle Informationen im Paket benötigen, wie Ort, Geschwindigkeit (Größe + Richtung) und Zeit. Dann können Sie den Puck dort und dann platzieren und den Puck von der Vergangenheit in die Gegenwart bewegen. Ich bin übrigens im Chat
John McDonald