AW: Query an Gameserver
Du solltest dir mal TMemoryStream oder TByteStream anschauen.
Das was du da machst kann man damit um einiges übersichtlicher/besser/einfacher lösen. Deine ganzen Hexstrings sind total unnötig :duck: |
AW: Query an Gameserver
ja das hat super funktioniert :thumb:
Schlussendlich benötige ich doch noch eine bessere Variante um die Einzelnen Stellen der Antwort auszulesen. So, wie man die Informationen von einem Gameserver per Query abfragen kann, kann man auch die globale serverliste beim Masterserver per Query abfragen. dieses Script habe ich ebenfalls in PHP vorliegen. Hier ein Link zum Script das die Serverliste anzeigt (Laden dauert etwas): http://last-templers.de/a2epoch/serverlist.php Nicht alle Server in der Liste funktionieren. Problem besteht in den letzten Bytes, die angeben ob der Server Passwortgeschützt ist und ob VAC aktiviert ist.
Code:
Ich kann das auch mit einer IF ELSE Abfrage lösen, aber irgendwie scheint mir das ganze ziemlich "dreckig" programmiert.
Bei Servern OHNE Passwort (647700;00;) funktioniert mein Code.
Bei Servern MIT Passwort (64770100;) funktioniert mein code natürlich nicht, da die letzten Strings des Array dann verschoben werden. Die einen mögen jetzt sagen: "Funktioniert doch, lass es so" . Andererseits verstehe ich die Funktion von Sir Rufo nicht so ganz. Ehrlich gesagt garnicht. Klar ist schon, dass er nur den Header ausliest und überprüft ob die Antwort Gesplittet ist oder nicht. Ich stocke schon an dem punkt, wie ich die ersten 4 Strings, die ja variable Längen haben, auslesen oder definieren soll. Das mit dem TMemoryStream hatte ich schon probiert. Da kam bei mir nur Gemurkse bei raus. Da blicke ich nicht durch. |
AW: Query an Gameserver
Ein kleines Beispiel:
Delphi-Quellcode:
Was du wahrscheinlich machen könntest wär sowas wie:
var
mem: TMemoryStream; intVar, intVarLesen: Integer; boolVar, boolVarLesen: Boolean; begin boolVar := true; intVar := 123; mem := TMemoryStream.Create; try // Integer schreiben mem.Write(intVar, SizeOf(Integer)); // Boolean schreiben mem.Write(boolVar, SizeOf(Boolean)); // Vor dem auslesen den Stream an den Anfang positionieren mem.Position := 0; // Daten in gleicher Reihenfolge auslesen wie sie eingelesen wurden mem.Read(intVarLesen, SizeOf(Integer)); mem.Read(boolVarLesen, SizeOf(Integer)); finally mem.Free; end; end;
Delphi-Quellcode:
var
PreHeader: Array[0..3] of Byte; Header: Byte; Protocol: Byte; tmpChar: AnsiChar; ServerName: AnsiString; begin mem.SetSize({größe des Antwortpakets}); udp1.ReceiveBuf(mem.Memory^, mem.Size); mem.Position := 0; mem.Read(PreHeader[0], SizeOf(PreHeader)); // FF FF FF FF mem.Read(Header, SizeOf(Byte)); mem.Read(Protocol, SizeOf(Byte)); // Aus folgenden kann/sollte man ne Funktion machen ServerName:= ''; repeat mem.Read(tmpChar, SizeOf(AnsiChar)); if (tmpChar <> #0) then ServerName:= ServerName + tmpChar; until tmpChar = #0; // usw.. end; |
AW: Query an Gameserver
Zitat:
Zitat:
Zitat:
Zitat:
Aber so lange wir nicht wissen wie Dein Programm jetzt aussieht, ist das eigentlich nur unverbindliches Geblubber. [OT] Bevor man sich an's programmieren macht, ist es kein Fehler zunächst einmal die zu verarbeitenden Daten zu analysieren, und diese Datenstruktur zu beschreiben. Oft ergeben sich dan ein oder zwei (Record-)Definitionen mit denen dann auf einmal alles ganz einfach ist. Ein kleines Beispiel hierzu ist #32. [/OT] Gruß K-H |
AW: Query an Gameserver
Das Funktionierende war auf die Verbindungsproblematik bezogen. Dies funktioniert jetzt schon soweit, dass wenn der Server nicht erreichbar ist, einfach nichts ausgewertet wird. Ändere ich den Queryport aber auf den Spielport zB, dann hängts immernoch.
Delphi-Quellcode:
da würde spätestens unten bei den Bytes nach den Strings dann wieder die Problematik aufkommen:
ServerName:= '';
repeat mem.Read(tmpChar, SizeOf(AnsiChar)); if (tmpChar <> #0) then ServerName:= ServerName + tmpChar; until tmpChar = #0; Mit Passwort: 8A 84 01 40 00 64 77 01 00 8A84 = Version 01 = Spieler aktuell 40 = Spieler max. 00 = Bots 64 = Typ 77 = Environment 01 = Passwort 00 = VAC status Ohne Passwort: 8A 84 01 40 00 64 77 00 8A84 = Version 01 = Spieler aktuell 40 = Spieler max. 00 = Bots 64 = Typ 77 = Environment --> PASSWORT FEHLT 00 = VAC status Ebenso verhällt es sich bei den aktuellen Spielern auf dem Server. die werden auch als 00 gesendet und somit würden sie dann nur als "stop" für den Stream behandelt werden. |
AW: Query an Gameserver
Ich hab mal aus Deinem HexDump ein Beispiel heraus gefischt:
Code:
Ist der Passwortkenner jetzt nicht gesetzt (x00) oder fehlt er? Und wenn Ja, dan gibt es also mind. 2 verschiedene Satztypen?
8A84
00 00 00 - Bots 00 - Typ 00 - Enviroment 00 - Passwortkenner(?) 00 - VAC status? 00 00 00 C4 Gruß K-H |
AW: Query an Gameserver
Wenn man das so betrachtet, dann macht das mit dem Zerlegen und dem ";" und dem Array am Ende keinen Sinn mehr.
Dann wäre es ja sinnvoller im oberen Teil einfach die ersten 4 variablen Strings zu lesen mit den 00en, danach die Bytes direkt an ihrer Position auszuwerten. Dann ist es egal ob 00 dort steht oder 0000 denn dann stehen die ersten 00 für 0 Spieler und die zweiten zB für 0 Bots. |
AW: Query an Gameserver
Also dieses ganz Rätselraten durch das falsche Zuordnen der einzelnen Bytes ist ja nicht zum Aushalten.
Nimm Papier und Bleistift zu Hand und mach dir mal eine Tabelle mit dem Satzaufbau (Name, Typ, Anzahl der Bytes, Bytes) und trage dann die empfangenen Bytes dort nach den vorgegebenen Regeln ein. So wie ich die Dokumentation lese und verstehe gibt es da nichts mit einem Passwort Feld. Nach Environment (Byte) kommt Visibility (Byte) und danach VAC (Byte). Also frisch ans Werk, das hilft dann auch bei der Umsetzung. |
AW: Query an Gameserver
Zitat:
Code:
Visibility byte Indicates whether the server requires a password:
0 for public 1 for private |
AW: Query an Gameserver
Zitat:
|
AW: Query an Gameserver
Ich hoffe du hast es so gemeint. Ja... kein Papier :oops:
|
AW: Query an Gameserver
Ich bin immer noch komplett baff, was du mit diesen ganzen völlig beknackten Umwandlungen nach Hex willst. Die sind ÜBER! Hier, das folgende zusammengetippert, indem ich mir die Struktur von der Valve-Seite in meinen Editor kopiert habe, und daran den Record orientiert. Den mit Hilfe der Typinfos befüllen, und die Sache ist gelutscht. Was ein Gehampel hier.
Delphi-Quellcode:
(Komplett ungetestet und im Editor geschrieben. Mag gut sein, dass da 1-2 Dinge nicht 100%ig hin kommen. Aber NOCH mehr vorkauen geht fast nicht.)
TInfoRequest = packed record
RequestHeader: Integer; PacketHeader: AnsiChar; Payload: array[0..19] of AnsiChar; end; TInfoResponse = record ResponseHeader: Integer; PacketHeader: Byte; Protocol: Byte; Name: String; Map: String; Folder: String; Game: String; ID: SmallInt; Players: Byte; MaxPlayers: Byte; Bots: Byte; ServerType: Byte; Environment: Byte; Visibility: Byte; VAC: Byte; Version: String; end; TBufferArray = array[0..1400] of Byte; function GetString(aBuffer: TBufferArray; var aIndex: Integer): String; var b: Byte; begin result := ''; repeat Move(aBuffer[aIndex], b, SizeOf(b)); if b <> 0 then result := result + IntToStr(b); inc(aIndex); until b = 0; end; procedure GetInfo; var request: TInfoRequest; response: TInfoResponse; buffer: TBufferArray; i: Integer; begin request.RequestHeader := -1; request.PacketHeader := 'T'; request.Payload := 'Source Engine Query'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring request.Payload[19] := #0; Udp1.SendBuf(request, SizeOf(TInfoRequest)); Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Protocol, SizeOf(response.Protocol)); inc(i, SizeOf(response.Protocol)); response.Name := GetString(buffer, i); response.Map := GetString(buffer, i); response.Folder := GetString(buffer, i); response.Game := GetString(buffer, i); Move(buffer[i], response.ID, SizeOf(response.ID)); inc(i, SizeOf(response.ID)); Move(buffer[i], response.Players, SizeOf(response.Players)); inc(i, SizeOf(response.Players)); Move(buffer[i], response.MaxPlayers, SizeOf(response.MaxPlayers)); inc(i, SizeOf(response.MaxPlayers)); Move(buffer[i], response.Bots, SizeOf(response.Bots)); inc(i, SizeOf(response.Bots)); Move(buffer[i], response.ServerType, SizeOf(response.ServerType)); inc(i, SizeOf(response.ServerType)); Move(buffer[i], response.Environment, SizeOf(response.Environment)); inc(i, SizeOf(response.Environment)); Move(buffer[i], response.Visibility, SizeOf(response.Visibility)); inc(i, SizeOf(response.Visibility)); Move(buffer[i], response.VAC, SizeOf(response.VAC)); inc(i, SizeOf(response.VAC)); response.Version := GetString(buffer, i); // In response steht jetzt alles richtig schön drin. Nicht als Hexkacke, sondern sauber als der Typ, den Valve vorgibt. end; Edit: War alles hübsch eingerückt, aber der "Beautifyer" der DP ist da ja anderer Meinung. Wenn man den Beitrag zitiert wird's besser. |
AW: Query an Gameserver
Und da sieht man doch schon, dass du immer noch nicht richtig in die Doku geschaut hast.
Wenn der Wert vom Typ BYTE ist, dann hat es da nur 1 (in Worten ein) Byte.
Hier Beispielhaften von der Valve Seite
Code:
Das sieht man, dass es den Wert für Extra Data Flags nicht geben muss!
FF FF FF FF 49 02 67 61 6D 65 32 78 73 2E 63 6F ÿÿÿÿI.game2xs.co
6D 20 43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65 m Counter-Strike 20 53 6F 75 72 63 65 20 23 31 00 64 65 5F 64 75 Source #1.de_du 73 74 00 63 73 74 72 69 6B 65 00 43 6F 75 6E 74 st.cstrike.Count 65 72 2D 53 74 72 69 6B 65 3A 20 53 6F 75 72 63 er-Strike: Sourc 65 00 F0 00 05 10 04 64 6C 00 00 31 2E 30 2E 30 e......dl..1.0.0 2E 32 32 00 .22. |
AW: Query an Gameserver
Zitat:
Dann sind notwendige Änderungen und Verbesserungsvorschläge leichter zu beschreiben. So ist es manchmal mehr oder weniger ein Rätselraten. |
AW: Query an Gameserver
Möchtest du dass ich das Projekt hochlade? Oder soll ich gesammten Code Posten?
|
AW: Query an Gameserver
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe jetzt mal den Beispiel-Response komplett aufgedröselt (Markdown-Format)
Code:
Im Anhang das gleiche als PDF gerendert
## A2S_INFO RESPONSE
### Example response for [Counter Strike: Source]: <pre> FF FF FF FF 49 02 67 61 6D 65 32 78 73 2E 63 6F ÿÿÿÿI.game2xs.co 6D 20 43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65 m Counter-Strike 20 53 6F 75 72 63 65 20 23 31 00 64 65 5F 64 75 Source #1.de_du 73 74 00 63 73 74 72 69 6B 65 00 43 6F 75 6E 74 st.cstrike.Count 65 72 2D 53 74 72 69 6B 65 3A 20 53 6F 75 72 63 er-Strike: Sourc 65 00 F0 00 05 10 04 64 6C 00 00 31 2E 30 2E 30 e......dl..1.0.0 2E 32 32 00 .22. </pre> #### Check for Protocol Field | Type | Bytes | Info :-- | --- | :-- | :-- Header | [long] | <pre>FF FF FF FF</pre> | -1 = Simple Response Format Payload | ∞ | <pre>49 02 67 61 6D 65 32 78 73 2E 63 6F 6D 20 43 6F<br>75 6E 74 65 72 2D 53 74 72 69 6B 65 20 53 6F 75<br>72 63 65 20 23 31 00 64 65 5F 64 75 73 74 00 63<br>73 74 72 69 6B 65 00 43 6F 75 6E 74 65 72 2D 53<br>74 72 69 6B 65 3A 20 53 6F 75 72 63 65 00 F0 00<br>05 10 04 64 6C 00 00 31 2E 30 2E 30 2E 32 32 00</pre> | #### Payload Data Field | Type | Bytes | Info :-- | --- | :-- | :-- Header | [byte] | <pre>49</pre> | I Protocol | [byte] | <pre>02</pre> | Name | [string] | <pre>67 61 6D 65 32 78 73 2E 63 6F 6D 20 43 6F 75 6E<br>74 65 72 2D 53 74 72 69 6B 65 20 53 6F 75 72 63<br>65 20 23 31 00</pre> | game2xs.com Counter-Strike Source #1 Map | [string] | <pre>64 65 5F 64 75 73 74 00</pre> | de_dust Folder | [string] |*<pre>63 73 74 72 69 6B 65 00</pre> | cstrike Game | [string] | <pre>43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65 3A 20<br>53 6F 75 72 63 65 00</pre> | Counter-Strike: Source ID | [short] | <pre>F0 00</pre> | 240 Players | [byte] | <pre>05</pre> | 5 Max. Players | [byte] | <pre>10</pre> | 16 Bots | [byte] | <pre>04</pre> | 4 Server type | [char] | <pre>64</pre> | d = dedicated Server Environment | [char] | <pre>6C</pre> | l = Linux Visibility | [byte] | <pre>00</pre> | 0 = public VAC | [byte] | <pre>00</pre> | 0 = unsecured Version | [string] | <pre>31 2E 30 2E 30 2E 32 32 00</pre> | 1.0.0.22 [byte]: https://developer.valvesoftware.com/wiki/Byte [char]: https://developer.valvesoftware.com/wiki/Byte [short]: https://developer.valvesoftware.com/wiki/Short [long]: https://developer.valvesoftware.com/wiki/Long [float]: https://developer.valvesoftware.com/wiki/Float [string]: https://developer.valvesoftware.com/wiki/String [Counter Strike: Source]: https://developer.valvesoftware.com/wiki/Counter_Strike:_Source |
AW: Query an Gameserver
Hab ich den ganzen Quatsch da oben jetzt umsonst geschrieben?
|
AW: Query an Gameserver
Zitat:
|
AW: Query an Gameserver
Zitat:
Das Konzept gefällt mir. Ich kannte die Funktion MOVE nicht und wie man merkt habe ich auch sonst wenig mit dynamischen Inhalten gearbeitet in Delphi. Ich habe ein wenig mit deinem Code rumprobiert und hab ihn halt um die IP und den Port erweitert. Das Ergebnis wenn ich zB. den Servernamen anzeigen lassen möchte ist jedoch mit mehrmaligem hin und her Probieren ernüchternd gewesen. Wahrscheinlich auf meine Dusseligkeit zurückzuführen.
Delphi-Quellcode:
Das Ergebnis sind nur viele Zahlen die ich meinen vorherigen Antworten vom Server nicht zuweisen konnte.
procedure GetInfo;
var request: TInfoRequest; response: TInfoResponse; buffer: TBufferArray; i: Integer; begin request.RequestHeader := -1; request.PacketHeader := 'T'; request.Payload := 'Source Engine Query'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring request.Payload[19] := #0; form4.udp1.RemoteHost:= form4.edit1.Text; form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Protocol, SizeOf(response.Protocol)); inc(i, SizeOf(response.Protocol)); response.Name := GetString(buffer, i); response.Map := GetString(buffer, i); response.Folder := GetString(buffer, i); response.Game := GetString(buffer, i); Move(buffer[i], response.ID, SizeOf(response.ID)); inc(i, SizeOf(response.ID)); Move(buffer[i], response.Players, SizeOf(response.Players)); inc(i, SizeOf(response.Players)); Move(buffer[i], response.MaxPlayers, SizeOf(response.MaxPlayers)); inc(i, SizeOf(response.MaxPlayers)); Move(buffer[i], response.Bots, SizeOf(response.Bots)); inc(i, SizeOf(response.Bots)); Move(buffer[i], response.ServerType, SizeOf(response.ServerType)); inc(i, SizeOf(response.ServerType)); Move(buffer[i], response.Environment, SizeOf(response.Environment)); inc(i, SizeOf(response.Environment)); Move(buffer[i], response.Visibility, SizeOf(response.Visibility)); inc(i, SizeOf(response.Visibility)); Move(buffer[i], response.VAC, SizeOf(response.VAC)); inc(i, SizeOf(response.VAC)); response.Version := GetString(buffer, i); form4.lblServerName.Caption := response.Name; Zitat:
Ich habe jetzt verstanden was Byte, String und short für mich zu bedeuten hat. Wenn ich nach der Länge "Byte" "lesen" lasse, wird auch nur ein Byte (2 physische Zahlen im optischen Sinne) gesucht bzw. ausgegeben. wenn ich 2 Byte lesen lasse, werden 4 Zahlen gelesen. Ich hoffe ich beruhige dich etwas damit und verärgere dich nicht noch mehr falls das nicht das war was du mir mitteilen wolltest. MFG |
AW: Query an Gameserver
Liste der Anhänge anzeigen (Anzahl: 1)
|
AW: Query an Gameserver
Würdest du uns Host und Port verraten? Dann würde ich meine Variante gerne mal selbst testen und ggf. debuggen. Vom Prinzip her sollte das an und für sich passen.
|
AW: Query an Gameserver
Zitat:
Es gibt einmal meinen privaten Server : Host: 5.45.97.44 Port: 2301 Und dann gibt es die große Serverliste: http://last-templers.de/a2epoch/serverlist.php Dauert etwas bis sie geladen hat, aber dann hat man auch noch XXX andere Server zum testen mit verschiedenen Einstellungen. Die Ports in der Liste sind immer die Query Ports. |
AW: Query an Gameserver
Vor ca. einer Minute von deinem Server abgerufen:
Code:
Einzige Änderung: Das IntToStr() in der GetString Prozedur muss natürlich Chr() heissen. Der Rest passt.
Header: -1
Header2: 73 Protocol: 17 Name: [L-T-S] Epoch Origins (1.0.5.1/125548) Map: Tavi Folder: arma2arrowpc Game: DayZ Epoch Origins ID: -31606 Players: 1 MaxPlayers: 25 Bots: 0 ServerType: 100 Environment: 119 Visibility: 0 VAC: 0 |
AW: Query an Gameserver
Zitat:
und in der GetInfo
Delphi-Quellcode:
fehlt das begin-end. So wie Du es hier machst wird nur der Versuch unterbunden SendBuf auszuführen.
if (form4.udp1.Connected) then
form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Aber die Zeilen danach werden immer ausgeführt, egal ob Connected oder nicht. |
AW: Query an Gameserver
Also LOB an Medium der mit so wenig Aufwand doch so viel erreicht hat in diesem Thema.
Vielen Dank dafür. 8-) Der hängt sich immernoch auf wenn ich auf einen falschen Port verbinde. Habe die Änderungen vorgenommen wie vorgeschlagen:
Delphi-Quellcode:
Ich weiss ich werde für die folgende Frage bestimmt gesteinigt. Aber warum kommt da eine negative Zahl bei der ID raus? kommt zumindest wenn ich wie folgt aufrufe:
form4.udp1.RemoteHost:= form4.edit1.Text;
form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then begin form4.Udp1.Send.... end;
Delphi-Quellcode:
Liegt das wieder an der 8A84 die falschrum sind?
IntToStr(response.ID)
|
AW: Query an Gameserver
Zitat:
Delphi-Quellcode:
begin request.RequestHeader := 'ÿÿÿÿ'; request.PacketHeader := 'T'; request.Payload := 'Source Engine Query'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring request.Payload[24] := #00; form4.udp1.RemoteHost:= form4.edit1.Text; form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Protocol, SizeOf(response.Protocol)); inc(i, SizeOf(response.Protocol)); response.Name := GetString(buffer, i); response.Map := GetString(buffer, i); response.Folder := GetString(buffer, i); response.Game := GetString(buffer, i); Move(buffer[i], response.ID, SizeOf(response.ID)); inc(i, SizeOf(response.ID)); Move(buffer[i], response.Players, SizeOf(response.Players)); inc(i, SizeOf(response.Players)); Move(buffer[i], response.MaxPlayers, SizeOf(response.MaxPlayers)); inc(i, SizeOf(response.MaxPlayers)); Move(buffer[i], response.Bots, SizeOf(response.Bots)); inc(i, SizeOf(response.Bots)); Move(buffer[i], response.ServerType, SizeOf(response.ServerType)); inc(i, SizeOf(response.ServerType)); Move(buffer[i], response.Environment, SizeOf(response.Environment)); inc(i, SizeOf(response.Environment)); Move(buffer[i], response.Visibility, SizeOf(response.Visibility)); inc(i, SizeOf(response.Visibility)); Move(buffer[i], response.VAC, SizeOf(response.VAC)); inc(i, SizeOf(response.VAC)); response.Version := GetString(buffer, i); //form4.edit1.Text := response.Name; end; //hier hin end; |
AW: Query an Gameserver
So habe ich es gemacht. Das kopierte Stückchen war wohl etwas unklar.
Delphi-Quellcode:
procedure Get_A2S_INFO;
var request: TInfoRequest; response: TInfoResponse; buffer: TBufferArray; i: Integer; begin request.RequestHeader := -1; request.PacketHeader := 'T'; request.Payload := 'Source Engine Query'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring request.Payload[19] := #0; form4.udp1.RemoteHost:= form4.edit1.Text; form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Protocol, SizeOf(response.Protocol)); inc(i, SizeOf(response.Protocol)); response.Name := GetString(buffer, i); response.Map := GetString(buffer, i); response.Folder := GetString(buffer, i); response.Game := GetString(buffer, i); Move(buffer[i], response.ID, SizeOf(response.ID)); inc(i, SizeOf(response.ID)); Move(buffer[i], response.Players, SizeOf(response.Players)); inc(i, SizeOf(response.Players)); Move(buffer[i], response.MaxPlayers, SizeOf(response.MaxPlayers)); inc(i, SizeOf(response.MaxPlayers)); Move(buffer[i], response.Bots, SizeOf(response.Bots)); inc(i, SizeOf(response.Bots)); Move(buffer[i], response.ServerType, SizeOf(response.ServerType)); inc(i, SizeOf(response.ServerType)); Move(buffer[i], response.Environment, SizeOf(response.Environment)); inc(i, SizeOf(response.Environment)); Move(buffer[i], response.Visibility, SizeOf(response.Visibility)); inc(i, SizeOf(response.Visibility)); Move(buffer[i], response.VAC, SizeOf(response.VAC)); inc(i, SizeOf(response.VAC)); response.Version := GetString(buffer, i); end; end; |
AW: Query an Gameserver
Zitat:
Statt "SmallInt" muss der Typ von Id "Word" sein. Dann ist nicht nur die Zahl positiv, sondern der Wert stimmt auch und ist dann 33930. |
AW: Query an Gameserver
Vielen Dank , das hat auch super geklappt.
Hab jetzt mal rumgespielt und versucht die A2S_PLAYER Daten zu bekommen. Das Funktionierte vorher ja auch schon soweit, dass die aktuelle Spielerzahl ausgegeben wurde. an dem selben Punkt bin ich jetzt mit der neuen Funktion die ich etwas umgebaut habe.
Delphi-Quellcode:
TPlayerResponse = record
ResponseHeader: Integer; PacketHeader: Byte; Challenge: Integer; PlayerCount: Byte; PlayerIndex: Byte; PlayerName: String; PlayerScore: Integer; PlayerDuration: Integer; end;
Delphi-Quellcode:
Ich bekomme sogar das D zurückgeliefert. Hatte echt Schwierigkeiten das Challenge in den Payload zu bekommen, aber am Ende hat es dann doch geklappt und die Spielerzahl wird wieder ausgegeben.
procedure Get_PLAYER_INFO;
var request: TInfoRequest; response: TPlayerResponse; buffer: TBufferArray; i: Integer; y: integer; begin request.RequestHeader := -1; request.PacketHeader := 'U'; request.Payload := 'FFFFFFFF'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring form4.udp1.RemoteHost:= form4.edit1.Text; form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Challenge, SizeOf(response.Challenge)); inc(i, SizeOf(response.Challenge)); i := 0; request.Payload[0] := chr(buffer[5]); request.Payload[1] := chr(buffer[6]); request.Payload[2] := chr(buffer[7]); request.Payload[3] := chr(buffer[8]); form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.PlayerCount, SizeOf(response.PlayerCount)); inc(i, SizeOf(response.PlayerCount)); for y := 0 to response.PlayerCount do begin Move(buffer[i], response.PlayerIndex, SizeOf(response.PlayerIndex)); inc(i, SizeOf(response.PlayerIndex)); Move(buffer[i], response.PlayerName, SizeOf(response.PlayerName)); inc(i, SizeOf(response.PlayerName)); Move(buffer[i], response.PlayerScore, SizeOf(response.PlayerScore)); inc(i, SizeOf(response.PlayerScore)); Move(buffer[i], response.PlayerDuration, SizeOf(response.PlayerDuration)); inc(i, SizeOf(response.PlayerDuration)); end; form4.lblServerHeader.Caption := chr(response.PacketHeader); // D = Antwort -> Playerinfos kommen end; end; Jetzt wollt ich natürlich weil ich einfach musste noch die Spielerdaten haben. Das schien mir auf den ersten Blick auch ganz einfach. Nimmste einfach ne Schleife und lässt ihn durch laufen und jedes Mal die Daten weiter Lesen. Hab mir ne Listbox vorne mit aufs Form gelegt und dann wollte ich die Listbox mit den SpielerNAMEN füllen.
Delphi-Quellcode:
Jedes Mal wenn ich auslesen möchte kommt ein Fehler.
form4.ListBox1.Items.Add(response.PlayerName)
EDIT: Das mit dem TimeOut funktioniert immernoch nicht richtig in der anderen Funktion. |
AW: Query an Gameserver
@BadenPower
Dann ist der auf der Valve Seite falsch deklariert. Dort ist ID als "short" gelistet, und ganz oben steht in den Typkonventionen: short = 16 bit signed integer Ein 16 Bit ohne Vorzeichen kommt dort nichtmals vor. @Flodding Ich hoffe, dass abgesehen vom nacken funktionieren jetzt auch deutlich wurde, wo du deine Gedankenverknotungen hattest, und wie man prinzipiell mit solchen Dingen umgeht. Was das Ganze hier etwas gemein gemacht hat war, dass die Strings von variabler Länge sind. Wären sie mit festen Längen definiert, könnte man das ganze Geraffel mit dem Move() weg lassen, und das RecieveBuffer() direkt eine TInfoResponse Variable füllen lassen, da die Antwort dann "binär" 1:1 zu der Recordstruktur passen würde (bzw. umgekehrt). Dann wäre das nen Einzeiler. Edit: Okay, du braucht mehr Grundlagen!!! 0xFFFFFFFF ist nicht 'FFFFFFFF'! Das erste ist eine 32 Bit Zahl in Hex-Darstellung, deins ist ein String. Dein String wäre in Hex 0x4646464646464646, also zu lang und völlig falsch. Du solltest dich bevor du mit Protokollen überhaupt anfängst dringend mit Datentypen und -darstellung befassen. |
AW: Query an Gameserver
Hab den Fehler gefunden.
Delphi-Quellcode:
kann ja nicht gehen... wofür hab ich denn EXTRA ne GETSTRING Funktion geschenkt bekommen?
Move(buffer[i], response.PlayerName, SizeOf(response.PlayerName)); inc(i, SizeOf(response.PlayerName));
Delphi-Quellcode:
Muss es natürlich heissen.
response.PlayerName := GetString(buffer, i);
Die Abfrage mit den Spielernamen funktioniert nun auch:
Delphi-Quellcode:
Ja mir fehlen viele Grundlagen was dieses Thema angeht. Ich hatte ehrlich gesagt auch nicht damit gerechnet vor solch Problemen (aus meiner Sicht jetzt) zu stehen als ich meinen kleinen CommunityLauncher angefangen habe zu basteln. Grundsätzlich wollte ich auch NUR die aktuelle Anzahl der Spieler anzeigen lassen, aber es taten sich mit einmal alle Möglichkeiten auf und dadurch ist es erst so eskaliert (wenn dies dann das richtige Wort ist)
procedure Get_PLAYER_INFO;
var request: TInfoRequest; response: TPlayerResponse; buffer: TBufferArray; i: Integer; y: integer; begin request.RequestHeader := -1; request.PacketHeader := 'U'; request.Payload := 'FFFFFFFF'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring form4.udp1.RemoteHost:= form4.edit1.Text; form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if (form4.udp1.Connected) then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.Challenge, SizeOf(response.Challenge)); inc(i, SizeOf(response.Challenge)); i := 0; request.Payload[0] := chr(buffer[5]); request.Payload[1] := chr(buffer[6]); request.Payload[2] := chr(buffer[7]); request.Payload[3] := chr(buffer[8]); form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader)); Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader)); Move(buffer[i], response.PlayerCount, SizeOf(response.PlayerCount)); inc(i, SizeOf(response.PlayerCount)); for y := 0 to response.PlayerCount - 1 do begin Move(buffer[i], response.PlayerIndex, SizeOf(response.PlayerIndex)); inc(i, SizeOf(response.PlayerIndex)); response.PlayerName := GetString(buffer, i); Move(buffer[i], response.PlayerScore, SizeOf(response.PlayerScore)); inc(i, SizeOf(response.PlayerScore)); Move(buffer[i], response.PlayerDuration, SizeOf(response.PlayerDuration)); inc(i, SizeOf(response.PlayerDuration)); form4.ListBox1.Items.Add(response.PlayerName); end; form4.lblServerHeader.Caption := chr(response.PacketHeader); // D = Antwort -> Playerinfos kommen end; end; Bis jetzt habe ich immer nur kleinere Tools gebastelt die mit dann im Alltag Kleinigkeiten abnehmen oder Dateieinhalte ändern und sowas. Da brauchte ich solch Teufelszeug noch nie und dementsprechend gering sind meine Erfahrungen damit. Wobei ich sagen muss, dass ich in der Vergangenheit wohl auch eher umständlich Programmiert habe als effizient. Ich habe es auch schon vorher erwähnt. Ich bin kein Professioneller Programmierer und werds leider auch niemals werden. Aber genau dafür gibt es solche Foren, wo Menschen die Wissen haben, es auch teilen können mit denen die es nicht haben oder noch nicht genug. Trotzdem möchte ich mich hier nochmal bei jedem bedanken der an diesem Thema seinen Beitrag geleistet hat und hoffe, dass zukünftige Forumbesucher mit der selben Problematik es hierdurch leichter haben. Am Ende bleibt ein Problem übrig... Zitat:
|
AW: Query an Gameserver
Zitat:
Aber ich habe auch die Ausgabe von dem folgenden Link angeschaut: http://last-templers.de/a2epoch/query/query.php Dort wird die ID auch positiv als 33930 dargestellt. Und eine negative ID macht in meinen Augen doch auch keinen Sinn. Daher bin ich eigentlich der Meinung, dass es ein "Word" sein müsste. |
AW: Query an Gameserver
Nein, was es ist ist eindeutig von Valve festgelegt: 16 bit signed integer
Was du daraus interpretierst ist und bleibt deine Sache. Du kannst ja auch die ID als HEX-Wert ausgeben, oder daraus ein Bild erzeugen, oder oder oder. Es ist einfach eine eindeutige ID und das wichtigste ist, dass beim Austausch mit dem Rest der Welt, die korrekte Bytefolge zu dieser ID wieder zurückgegeben wird. In der Schnittstelle würde ich das immer so belassen, wie es vorgegeben ist ... |
AW: Query an Gameserver
Zitat:
Ist es aber auch richtig? Ich wage daran zu zweifeln, da dort die "Steam Application ID" ausgegeben werden soll und diese beginnen bei 0. Und bei diesem Gameserver mit diesem Port, den uns Flodding genannt hat ist die AppId laut Steamdb.info nun mal 33930 und nicht -31606. |
AW: Query an Gameserver
Das ist doch lediglich eine Frage der Interpretation, 2 Byte bleiben 2 Byte.
|
AW: Query an Gameserver
Zitat:
Delphi-Quellcode:
Normalerweise sollte die Funktion WaitForData() true zurückgeben wenn der Server bereit ist. Da bei mir mit D2007 aber immer false geliefert wird, mache das wie oben gepostet.
procedure TForm4.Get_A2S_INFO;
var Request: TInfoRequest; Response: TInfoResponse; Buffer: TBufferArray; I: Integer; CheckTime: TDateTime; begin Request.RequestHeader := 'ÿÿÿÿ'; Request.PacketHeader := 'T'; Request.Payload := 'Source Engine Query'; Request.Payload[19] := #0; Udp1.RemoteHost:= Edit1.Text; Udp1.RemotePort:= Edit2.Text; Udp1.Open; if (Udp1.Connected) then begin Udp1.SendBuf(Request, SizeOf(TInfoRequest)); CheckTime := Now(); Udp1.WaitForData(5000); if (SecondsBetween(Now(),CheckTime) <= 1) then begin Udp1.ReceiveBuf(Buffer, SizeOf(Buffer)); I := 0; Move(Buffer[I], Response.ResponseHeader, SizeOf(Response.ResponseHeader)); Inc(I, SizeOf(Response.ResponseHeader)); //... end; end; end; Kann das einmal jemand prüfen, ob bei höheren Delphi-Versionen True zurückgegeben wird? Wenn ja dann könnte man das Workaround in höheren Versionen weglassen. |
AW: Query an Gameserver
EDIT: Frage nach der Antwort gepostet. :oops:
Hallo! Immernoch die "nicht erreichbar" Problematik. Mein aktueller Code um die Verbindung aufzubauen:
Delphi-Quellcode:
Wenn der Port korrekt ist, wird 1 Sekunde gewartet und dann der "buffer" gelesen.
form4.udp1.RemoteHost:= '5.45.97.44';
form4.udp1.RemotePort:= '2301'; form4.udp1.Open; if form4.udp1.Connected then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); form4.udp1.WaitForData(1000); if Length(buffer)<1 then begin form4.udp1.Close; exit; end; Ist der Port aber kein QueryPort:
Delphi-Quellcode:
Das Programm hängt sich auf.
form4.udp1.RemoteHost:= '5.45.97.44';
form4.udp1.RemotePort:= '1111'; // Port geändert form4.udp1.Open; if form4.udp1.Connected then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); form4.udp1.WaitForData(1000); if Length(buffer)<1 then begin form4.udp1.Close; exit; end; Ich hab schon nachgelesen und scheinbar hängt es sich nicht wirklich auf, sondern lauscht weiterhin und wartet auf Antwort. Ich bin auf diese Option gestoßen:
Delphi-Quellcode:
Damit soll das angeblich funktionieren, jedoch bekomme ich dann immer die Meldung vom "OnError"-Event, dass keine Verbindung aufgebaut werden konnte.
form4.udp1.BlockMode := bmNonBlocking
Delphi-Quellcode:
Irgendwelche Ideen?
procedure TForm4.udp1Error(Sender: TObject; SocketError: Integer);
begin showmessage('Could not connect. The server you are trying to query might be down'); udp1.Active:= false; end; |
AW: Query an Gameserver
Ich habe das OnError Event erstmal deaktiviert, weil der immer ausgelöst wurde wenn WaitForData zu lange gedauert hat.
Habs so ausprobiert jetzt:
Delphi-Quellcode:
Bleibt immernoch hängen und wartet nicht nur 1 Sekunde.
form4.udp1.RemoteHost:= form4.edit1.Text;
form4.udp1.RemotePort:= form4.edit2.Text; form4.udp1.Open; if form4.udp1.Connected then begin form4.Udp1.SendBuf(request, SizeOf(TInfoRequest)); CheckTime := Now(); form4.udp1.WaitForData(1000); if (SecondsBetween(Now(),CheckTime) <= 1) then begin form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer)); i := 0; ... ... |
AW: Query an Gameserver
Da es mir Schmerzen verursacht, es immer wieder zu lesen: Es gibt keine UDP-Verbindung! UDP ist ein verbindungsloses Protokoll. Die Verbindung gibt es auf Anwendungsebene ... also das was du gerade programmierst.
Bezüglich des Fehlers: Guck dir mal den Errorcode an ... eventuell steht da nur so WSAEWOULDBLOCK drin. |
AW: Query an Gameserver
Zitat:
Also
Delphi-Quellcode:
Weil wenn der TimeOut nur 1 Sekunde (Wert 1000) sein soll, dann ist der Unterschied zwischen CheckTime und Now() in der If-Anweiung IMMER kleiner oder gleich 1 Sekunde.
form4.udp1.WaitForData(5000);
if (SecondsBetween(Now(),CheckTime) <= 1) then begin ... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:10 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz