Autor |
Beitrag |
Mortal-Shadow
      
Beiträge: 110
|
Verfasst: Mi 12.11.08 11:04
Hallo,
ich versuche ein Weltraumshooter (jeder kennt sie - halt son einfaches Teil) Netzwerkfähig zu programmieren.
So etwas funktioniert ja bekanntermasen, indem man alle paar Zeitabstände die Positionswerte neu berechnet.
Diese Zeitabstände müssen allerdings auf beiden Computern gleich sein.
Doch wie synchronisiere ich die Computer?
Meine Idee wäre, dass ein Timer (oder vllt auch Thread) alle 50 ms einen Zeitschritt macht.
Diese Schritte werden gezählt.
Zusätzlich sendet der Server im Sekundentakt ein Signal.
Die Programme vergleichen nun ihre Zeitschritte mit der vergangenen Zeit laut Server und warten oder holen ein paar Schritte nach, sodass sie wieder auf aktuellem Stand sind.
Allerdings könnte dies zu ruckeln führen.
Oder soll ich den Server bei jedem Zeitschritt ein Signal senden lassen - und der Server wartet dann auf die Antworten der Clients, bis jeder Rechner alle Positionen berechnet hat UND mindestens 50 ms vergangen sind?
Oder gibt es noch eine bessere Synchronisationsmöglichkeit?
Gruß,
MS.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 12.11.08 11:30
Grundsätzlich zählt das was der Server sieht. Wenn der Server einen anderen Standpunkt für ein Objekt sendet, wird dieses eben sofort dahin positioniert. Wenn dann mal ein Lag war sieht man diesen Sprung. Anders geht es aber nicht sinnvoll, denn wenn nicht nur der Stand des Servers zählen würde, dann könnte man Lags vortäuschen und so schummeln.
Wieviele Zeitabschnitte auf den Clients vergangen sind oder nicht ist doch egal. Wenn auf dem einen PC nur 5 vergangen sind in der Sekunde, dann werden diese 5 Positionen eben alle 0,2 Sekunden berechnet, wenn auf dem anderen PC 20 vergangen sind alle 0,05 Sekunden. Die Position zu jedem Zeitpunkt ist doch aber davon unabhängig wie oft diese aktualisiert wurde. Es müssen doch gar nicht überall genauso viele Schritte durchgeführt werden.
|
|
Mortal-Shadow 
      
Beiträge: 110
|
Verfasst: Mi 12.11.08 16:12
Ja, das nur der Server zählt ist mir klar, ich habe mir das so vogestellt, dass die Clients immer nur eine Aktion senden (Bewegen nach links, schiessen...).
Der Server bestätigt dann und sendet die Aktion an alle Clients.
Die Frage ist eher - woher weis ich wie viele Zeitschritte ein Client in der Sekunde schafft?
Es ist ja leider keine feste Zeiteinheit, da er je nach Anzahl der Momentan vorhandenen Objekte mehr oder weniger schaffen wird.
Wenn sich ein Raumschiff pro Sekunde um 50 Einheiten nach links bewegen soll - woher weis ich wie lang ein Zeitschritt ist und somit wie viel sich das Raumschiff bewegen soll?
Sollte ich das vielleicht mittels gettickcount() ermitteln wie viel Zeit nach jedem Schritt vergangen ist?
PS: Wie kann ich AntiVir verständlich machen, dass mein Programm NICHT das Backdoorprogramm BDS/Hubigon ist und es bei jedem Compilieren eine Warnung schickt, ohne Zugleich komplett auf Antivir zu verzichten?
|
|
Jann1k
      
Beiträge: 866
Erhaltene Danke: 43
Win 7
TurboDelphi, Visual Studio 2010
|
Verfasst: Mi 12.11.08 16:28
Das, was du mit den zeitschritten meinst, nennt sich TimeBasedMovement und geht recht einfach (mal die SuFu dazu bemühen). Damit geht es, dass ein Raumsciff sich mit 50 Picel pro Sekunde bewegt unabängig von der Framerate
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 12.11.08 16:37
Mortal-Shadow hat folgendes geschrieben : | PS: Wie kann ich AntiVir verständlich machen, dass mein Programm NICHT das Backdoorprogramm BDS/Hubigon ist und es bei jedem Compilieren eine Warnung schickt, ohne Zugleich komplett auf Antivir zu verzichten? |
Auch wenn das nicht zum Thema passt: Bist du sicher, dass sich der Virus nicht auf deinem System befindet? (Und die frische Exe infiziert.)
Um einen Fehlalarm zu melden kannst du bei Avira unter folgender Adresse eine solche Datei hochladen:
analysis.avira.com/samples/index.php
Bei Typ stell dann "Verdacht auf Fehlalarm" ein. Vorher aktualisiere aber Antivir nochmal.
Und ansonsten: Bei weiteren Fragen, die nicht zum Thema gehören erstelle bitte einen neuen Thread.
|
|
Mortal-Shadow 
      
Beiträge: 110
|
Verfasst: Mi 12.11.08 18:02
Timebased Movement klingt doch ganz gut.
Allerdings stellt sich die Frage was ich nehmen soll - gettickcount() oder Querryperformancecounter()?
Ersteres soll schnell aber ungenau sein, letzteres genau aber Rechenintensiv.
Und eben wegen Ungenauigkeiten noch die Frage, in welchen Zeitabständen der Server alle Positionen neu an
die Clients übertragen soll, um Abweichungen wieder auszugleichen?
|
|
Jann1k
      
Beiträge: 866
Erhaltene Danke: 43
Win 7
TurboDelphi, Visual Studio 2010
|
Verfasst: Mi 12.11.08 19:32
Ich selber bin bisher mit gettickcount() gut ausgekommen. Ich würde es von deiner Anwendung abhängig machen, wenn die überall mit 80 fps läuft und Querryperformancecounter() sie nur auf 60 fps drückt (o.ä.) spricht ja nichts dagegen es zu nutzen.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 12.11.08 23:14
Im Endeffekt brauchst Du sogar nicht mal alle Positionen neu zu übertragen, wenn Du mit Interpolation kombiniert mit Timebased Movement arbeitest.
Was ich meine: Am Anfang gehen alle davon aus, dass sich ein Asteroid an Position P befindet und mit Geschwindigkeit v bewegt. Jede Aktion in deinem System bekommt nun IMMER einen Zeitstempel. Wenn jetzt der Spieler auf Abschuss drückt, so generiert der Client dafür einen Zeitstempel, von dem ER denkt, wann es war, und sendet diesen zusammen mit dem Hinweis "Nutzer hat geschossen" ein Paket zum Server. Wenn nun jedwede Kommunikation IMMER einen Zeitstempel besitzt, kann man das Rauschen der Laufzeit-Unterschiede drastisch reduzieren und so beide Uhren auf Client und Server praktisch synchron arbeiten lassen.
Der Server kann nun aber auch gleichzeitig die Client-Zeit so in seine eigene Zeit transformieren, so dass man den eigentliche "Serverzeitpunkt" zu dem der Client den Befehl vermeintlich ausgeführt hat, umgerechnet bekommt. Der Server kann nun anhand dieser Information zurückrechnen und überprüfen, ob der Spieler getroffen hat, oder nicht.
Darauf aufbauend kann der Server nun neue "Synchronisationspakete" erstellen, die einerseits Aktualisierungen der Welt (Komet hat seine Bahn um 08:15 geändert) aber auch Synchronisierungsnachrichten enthalten (Der Server gibt vor, dass es jetzt 11:47 [mm:hh] ist).
Diese Art der Zeit-Synchronisierung funktioniert umso besser, je präziser die Quelle für Informationen über die zu synchronisierenden Informationen ist. Im Extremfall kann man damit erreichen, dass zwei Uhren auf die Nanosekunde synchron laufen, selbst wenn diese nur einmal pro Sekunde gestellt werden. Die interne Genauigkeit (mit der bei sowas dann gearbeitet wird, sind dann oftmals bis zu 64 Bit  Rundungsverluste).
Auf diese Art und Weise funktionieren übrigens auch NTP-Server: Einer gibt ein Zeitsignal vor und alle anderen gehen unterschiedlich viel falsch. Man korrigiert hier nämlich nicht Laufunterschiede sprunghaft, sondern gleicht nach und nach an.
MfG,
BenBE.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Mortal-Shadow 
      
Beiträge: 110
|
Verfasst: Do 13.11.08 10:42
Also mach ich es so, dass der Server zu begin gettickcount aufruft und an die Clienten sendet.
Die rufen daraufhin auch gettickcount auf und vergleichen beide Werte, sodass sie ihren imer in den des Servers umrechnen können.
Aber wie bekomme ich da die Verzögerungen beim synchronisationsversuch heraus?
(Wenn der Server sendet vergehen einige ms bis die Nachricht den Client erreicht, der muss diese Auswerten und selber gettickcount auswerten.)
Dies dürfte viele Millisekunden verfälschen.
Oder kann man die tatsächlich vernachlässigen?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 13.11.08 13:49
Der Client geht nach einer gewissen Zeit bei einem solchn Verfahren "quasi-parallel" zum Server, was nicht heißt, dass die beiden nicht den gleichen Zeitstempel anzeigen, aber zumindestdie Geschwindigkeit beider Uhren ist identisch. Da der Client an sich keine Verzögerungen im Netzwerk kennt. Dieses Problem eliminiert sich, wenn regelmäßig "verzögerungslose Antworten" vom Gegenüber gesendet werden.
In einem solchen Fall kann man anhand der RTT der Nachricht+Antwort den VermutlichenLaufunterschied berechnen und diesen bei der Zeitkorrektur z.B. serverseitig dem Client mitteilen. Also Server sendet eine Nachricht an Client "Abschuss kam um 1337, das Paket wirst Du vermutlich um 2342 erhalten haben."
Zu den auftretenden Latenzen: In einem lokalen Netzwerk kannst Du diese mit oftmals <5ms vernachlässigen. Im Internet kommt man bei einer guten Leitung auf Werte zwischen 10 (Fast Path), 40 (Normales DSL), 100 (DSL Light), 300-500 (Modem) Millisekunden. Selbst bei Spielen wie Tetris kann man aber noch bei etwa 100ms Latenz halbwegs spielen (ist merklich schwerer, aber lässt sich noch bewerkstelligen).
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
|