UDP Zu langsam?

Übersicht BlitzBasic Beginners-Corner

Neue Antwort erstellen

Cykid

Betreff: UDP Zu langsam?

BeitragDo, Jul 10, 2014 4:07
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo!

Ich habe folgendes Problem:
Ich beschäftige mich im Moment mit UDP und habe da leider noch nicht viel gemacht.
Mein Problem ist das der Client dem Server schickt "Hey, ich will mich nach rechts bewegen". Der Server verändert die X Position +1 und sendet sie dann im nächsten durchlauf an alle Clients.

Das ganze dauert aber 3,4 Sekunden bis sich mein Rechteck bewegt.

Das ganze hat leider ein Haufen Includes aber ich denke das ich das wichtigste hier zusammenpacke:

Der Serverteil der auf Nachrichten wartet:

BlitzBasic: [AUSKLAPPEN]
Function rec_Message()

IP_Adresse = RecvUDPMsg(stream)



If IP_Adresse <> 0 Then
Color 0,0,255


msg$ = ReadLine(stream)

;Handshake
If msg$ = "Hello"
c.clients = New clients
clientsList = clientsList +1
c\x = Rand(0,200)
c\y = Rand(0,200)
c\IP = IP_Adresse
c\name$ = ReadLine(stream)
Print ">|"+IP_Adresse + " Connected"
Print ">|Client "+c\name$+ " has logged in"

Color 255,0,0
Print "*|Send Handshake to: "+c\IP

prepare_UDP(c\x)
prepare_UDP(c\y)
prepare_UDP(c\IP)
send_UDP(c\IP)

Print "*|Send x Position on: "+c\x
Print "*|Send y Position on: "+c\y
Print "*|Send IP to Client : "+c\IP
EndIf

;Timout
If msg$ = "Alive!"
For c.clients = Each clients
If c\ip = IP_Adresse
c\pingBuffer = 0
EndIf
Next
EndIf

;Movements

If msg$ = "MoveX"

value = ReadLine(stream)

For c.clients = Each clients
If c\ip = IP_Adresse
c\x = c\x + value
EndIf
Next

EndIf


EndIf


End Function


Der Senden Teil des Clients

BlitzBasic: [AUSKLAPPEN]
Function sendMovements()
If KeyDown(57) Then
prepare_UDP("MoveX")
prepare_UDP(1)
send_UDP()
EndIf
End Function


Der Senden Teil des Servers

BlitzBasic: [AUSKLAPPEN]
Function send_ClientsInfo()

d.clients = c.clients

For c.clients = Each clients

clientIp = c\IP

prepare_UDP("ClientInfo")
prepare_UDP(clientsList)

For d.clients = Each clients
prepare_UDP(d\x)
prepare_UDP(d\y)
prepare_UDP(d\name)
prepare_UDP(d\IP)
Next


send_UDP(c\IP)

Next

End Function


Der Empfangen Teil des Clients

BlitzBasic: [AUSKLAPPEN]
Function rec_Message()
IP_Adresse = RecvUDPMsg(stream)
If IP_Adresse <> 0 Then

msg$ = ReadLine(stream)

;Alle Abfragen

If msg$ = "ClientInfo"

Delete c.clients
counts = ReadLine(stream)

For x = 1 To counts
c.clients = New clients
c\x = ReadLine(stream)

c\Y = ReadLine(stream)
c\name$ = ReadLine(stream)

c\Ip = ReadLine(stream)

If c\Ip = myIp Then
px = c\x
py = c\y
Delete c.clients
EndIf

Next


EndIf


EndIf
End Function


Seht ihr vlt. woran es liegen kann das es Local mit nur einem Client so lange dauert?

Ich wäre euch sehr verbunden.

Viele Grüße,
ein hoffnungsloser Anfänger Very Happy

DAK

BeitragDo, Jul 10, 2014 8:10
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn es lokal mit nur einem Client so lahm ist, dann liegt es ziemlich sicher nicht an UDP. Hast du schon versucht, die Funktionen zu profilen? Lass jede Funktion 100 oder 1000 mal in einer For-Schleife laufen, und miss die Zeit, die es dafür braucht (mittels MilliSecs()). Dann weist du, welche Funktion du beschleunigen musst.

Keine deiner Funktionen sollte länger als 1 ms pro Durchlauf brauchen.

Auch kannst du in die Funktionen selbst einen Aufrufzähler einbauen und so sehen, ob eine deiner Funktionen zu oft aufgerufen wird (vielleicht hast du irgendwo eine Schleife zu viel oder so).
Gewinner der 6. und der 68. BlitzCodeCompo

BladeRunner

Moderator

BeitragDo, Jul 10, 2014 10:30
Antworten mit Zitat
Benutzer-Profile anzeigen
Grade UDP sollte wirklich schnell sein, Speedprobleme gibt es eher mit TCP/IP, da Blitz hier wartet bis die Sendebestätigung da ist. Der Fehler wird also, wie DAK es schon erwähnte, wohl eher woanders im Code zu finden sein.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Cykid

BeitragDo, Jul 10, 2014 13:24
Antworten mit Zitat
Benutzer-Profile anzeigen
Es sollte nicht am Server liegen, der läuft in unter einer MS.

Beim Client kann ich mir nicht erklären woran es liegt.

Er verbindet sich und sendet "hallo" und seinen Namen. Der Server begrüßt ihn zurück, trägt ihn ein, und sendet die Koords.

Die bekommt der Client umgehend.

Er beginnt nun einmal pro Schleifendurchlauf (Durch Timer auf 60 FPS begrenzt) ein "Alive!" zu senden damit der Server ihn nicht "Timeoutet"

Der Server sendet nun einmal pro Schleifendurchlauf alle Infos an alle Clients.

*Pseudocode*
Code: [AUSKLAPPEN]

senderliste = clientsliste

for clientliste = each clientliste
 sende den header "ClientList"
 sende anzahl der Clients
 
 for senderliste = each senderliste
  sende x,y,name,ip

 next

next



Darauf löscht der Client seine alte Liste der Mitspieler und legt eine komplett neue an.
Stellt er dabei fest das die Ip = Seine Ip ist (Die der Server zu beginn den Client mitteilt) löscht er dieses Objekt raus und gibt sich die Daten (x,y)

Nun soll ein Rechteck an seinen X und Y Gezeichnet werden.

Wenn er die Leertaste drückt sendet er den Server "BewegeX" und dern wert '1'.

Der Server sucht in seiner Liste nach dem client und addiert seinen X Wert um den des Ursprungs.

Ich hoffe ihr könnt mir folgen Very Happy

Gibt es allein hierbei schon einen Logikfehler?

DAK

BeitragDo, Jul 10, 2014 13:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Heißt das, du schickst jedes Frame die Infos für alle Spieler, löscht die gesamte Spielerliste und erstellst sie neu? Lädst du auch alle Bilder usw. jedes Frame neu?

Du solltest nicht mehr als maximal 10x die Sekunde senden, eher Richtung 2-4 Mal die Sekunde. Den Rest musst du interpolieren / extrapolieren.

Auch sollst du nicht alles löschen und neu erstellen sondern nur die benötigten Teile überschreiben.

Noch eins: kanns sein, dass du beim Empfangen pro Frame nur ein Paket empfängst? Was du da machen musst ist um das Empfangen eine While-Schleife mit ReadAvail()>0 hängen. Damit kann er pro Frame mehrere Pakete lesen. Ansonsten hast du z.B. einen Client, der z.B. 2 FPS langsamer ist als der Server. Das heißt, pro Sekunde bleiben 2 Pakete liegen, die der Server geschickt, der Client aber nicht gelesen hat. Das heißt, pro Sekunde hinkt der Client dem Server um 2 Frames länger nach. Nach einer Minute sind das schon ganze 120 Frames oder 2 Sekunden.
Gewinner der 6. und der 68. BlitzCodeCompo

Cykid

BeitragDo, Jul 10, 2014 14:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Erstmal viel Dank für die Mühe meine Problematik zu verstehen! Smile

Also bei der Abfrage der Pakete so?

BlitzBasic: [AUSKLAPPEN]
Function rec_Message()
IP_Adresse = RecvUDPMsg(stream)
If IP_Adresse <> 0 Then

While ReadAvail (stream) > 0
msg$ = ReadLine(stream)

;Alle Abfragen

If msg$ = "ClientInfo"

Delete c.clients
counts = ReadLine(stream)

For x = 1 To counts
c.clients = New clients
c\x = ReadLine(stream)

c\Y = ReadLine(stream)
c\name$ = ReadLine(stream)

c\Ip = ReadLine(stream)

If c\Ip = myIp Then
px = c\x
py = c\y
Delete c.clients
EndIf

Next


EndIf
Wend


EndIf
End Function


Und das Senden des Servers reduzieren und die Clientverwaltung im Client optimieren, dann sollte es Funktionieren?

DAK

BeitragDo, Jul 10, 2014 15:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Jup, die Abfrage schaut schon etwas besser aus. Was noch gut wäre, wäre wenn du Sachen mit den passenden Typen verschickst. Im Moment verwendest du nur Line für alles. Verwende für Ints ReadInt/WriteInt und für Floats ReadFloat/WriteFloat. Damit sparst du dir einiges an Daten.

Falls du willst schau dir meinen Serializer an, der packt dir automatisch die Felder die du brauchst in Minimalform. Funktioniert mit allen Streams, also Files, UDP und TCP.

Wenn du nur beim Server das Senden reduzierst, aber beim Client nichts änderst, dann werden alle Sachen am Client ruckeln. Deswegen musst du beim Client aus den letzten empfangenen Daten extrapolieren. Das heißt, wenn du die Position eines Objekts plus seine Bewegungsrichtung bekommst, dann musst du beim Client die Objekte in diese Richtung weiter bewegen, um die Zeit bis zum nächsten Paket zu überbrücken.

Edit: Habe beim Serializer noch all das eingebaut, was hier fehlt, schau dir mal das Ganze als Sample-Code an, vielleicht hilft es dir.
Gewinner der 6. und der 68. BlitzCodeCompo

Cykid

BeitragDo, Jul 10, 2014 15:57
Antworten mit Zitat
Benutzer-Profile anzeigen
Habe nun das Senden reduziert auf alle 20 ms und beim Client die Bewegungen immer vorrausgerechnet und siehe da, Die Bewegungen sind Syncron und 'Weich'

Vielen dank dafür!

Ich mache mich mal an die Optimierungen und versuche daraus einen kleinen Multipayer TopDown shooter zu basteln Very Happy

DAK

BeitragDo, Jul 10, 2014 22:36
Antworten mit Zitat
Benutzer-Profile anzeigen
Alle 20 ms sind in den meisten Fällen (außer in einem kabelgebundenen LAN) auch zu häufig. 20 ms heißt 50 mal pro Sekunde. Das ist nur knapp weniger als die 60 FPS die du hast. Wenn du eh Client Simulation hast, dann solltest du auch mit deutlich weniger auskommen.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Hangman

BeitragFr, Jul 11, 2014 3:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Z.B. alle 2 Sekunden... nur um mal die Größenordnung auszusprechen.
Nicht dass aus alle 20ms ein alle 40ms wird^^
Ich habe Berthold gebrochen.

DAK

BeitragFr, Jul 11, 2014 11:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Alle 2 Sekunden sind für einen Top-Down-Shooter schon wieder etwas selten. Das Ziel ist es, so wenig Daten wie möglich so selten wie möglich zu senden, und dabei noch ein flüssiges Spiel zu behalten.

Was auch wichtig ist, ist dass nicht alle Daten gleich oft geschickt werden müssen. Du schickst ja im Moment alle Daten aller Spieler bei jedem Update. Dabei gibt es Daten, die sich nie ändern (z.B. der Spielername), Daten die sich selten ändern (z.B. Hitpoints oder die momentan getragene Waffe) und Daten, die sich oft ändern (z.B. die Laufrichtung). So macht es dann keinen Sinn, den Spielernamen jedes Frame neu zu schicken, da das nur unnötig viel Daten braucht.

Was auch Sinn macht, ist wenn du die Daten in Events und dauerhafte Sendung trennst. Dauerhafte Sendungen wären dann z.B. die Spielerposition, Events wären das Drücken der Schuss-Taste. Die dauerhaften Sendungen werden alle X ms geschickt, egal was passiert. Die Events werden nur einmalig dafür sofort geschickt, wenn sie eintreten. Auf diese Weise bleibt z.B. ein abgegebener Schuss nicht ewig in der Warteschleife für's nächste Update. Gleichzeitig wird auch nicht unnötiger Traffic im regelmäßigen Update verschwendet, wo sonst den Großteil der Zeit über drinnen stehen würde, dass der Spieler gerade nicht schießt.
Gewinner der 6. und der 68. BlitzCodeCompo
 

Hangman

BeitragFr, Jul 11, 2014 21:43
Antworten mit Zitat
Benutzer-Profile anzeigen
Theoretisch muss man ein einziges mal die absoluten Koordinaten übertragen, wenn man die Tastendrücke oder Richtungsänderungen sofort und natürlich jedes mal überträgt. Insofern sind 2s für ein Koordinatenupdate überhaupt nicht selten.
Ich habe Berthold gebrochen.

DAK

BeitragFr, Jul 11, 2014 22:23
Antworten mit Zitat
Benutzer-Profile anzeigen
So gesehen hast du wieder recht. Ich hab nur gemeint, wenn das alles ist, was man überträgt, dann sind 2 Sec schon sehr wenig.

Die absoluten Koordinaten sollte man schon regelmäßig übertragen (nicht nur ein Mal am Anfang), da Timing-Fehler sich über die Zeit zusammen sammeln und größere Fehler verursachen.
Gewinner der 6. und der 68. BlitzCodeCompo

Cykid

BeitragFr, Jul 25, 2014 3:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Das Problem ist nur das wenn man soviel über den Client macht das ganze sehr sehr anfällig für Manipulationen ist.

Hatte erst gedacht das ich z.b. über den Client berechnen lasse ob er getroffen wurde.

Als ich das ganze mit Freunden ausprobiert habe, habe ich mein eigenes Spiel mit Assembler umgeschrieben und aus ein 'jnz' ein 'jmp' gemacht, schon wars das Very Happy

Gibt es andere möglichkeiten oder ist es vollkommen legitim den Server berechnen zu lassen, wer getroffen wurde?

DAK

BeitragFr, Jul 25, 2014 7:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Hier macht sich am besten ein hybrider Ansatz. Du lässt alles sowohl am Client als auch am Server berechnen. Solange sich der Client gültig vom Server unterscheidet, werden seine Werte genommen, ansonsten gelten die Werte des Clients.

Das heißt, wenn der Client im letzten Netz-Frame nach rechts gelaufen ist, und der Server deswegen annimmt, dass der Client weiter nach rechts läuft, der Client aber stattdessen nach links läuft (aber nur eine realistische Distanz), dann gilt das, was der Client sagt. Wenn der Client aber nicht nur nach links, sondern doppelt so weit nach links gelaufen ist, wie er in der gegebenen Zeit dürfte, dann gilt das, was der Server sagt. Sinnvoll ist es auch hier eine kleine Toleranz (5% oder so) einzubauen, um für schwankende Latenz zu kompensieren. Oder man schickt die Zeit der Frames mit dem Frame mit (einfach den Wert von MilliSecs()) und vergleicht darauf, wie weit der Client in der Zeit kommen darf.

Die endgültige Berechnung wer getroffen wurde (sowie andere wichtige Berechnungen) sollten immer am Server passieren, da es sich sonst zu leicht schummelt. Auch sollte der Server z.B. alle Inputs von toten Spielern aktiv ignorieren, und nicht nur darauf vertrauen, dass der Client eines toten Spielers keine Daten mehr sendet.
Gewinner der 6. und der 68. BlitzCodeCompo

Neue Antwort erstellen


Übersicht BlitzBasic Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group