![]() |
Query an Gameserver
Hallo guten Morgen :cry:
Ich bastel seit ein paar Tagen an einem Launcher/Joiner für meinen Gameserver (Arma2 - Dayz). Dieser kann erkennen ob die richtigen Mods in der richtigern Version vorliegen und kann bei bedarf auch korrupte oder neue Dateien anfordern und Downloaden. Jetzt möchte ich im Launcher auch noch gerne anzeigen Lassen wie viele Spieler sich aktuell auf dem Server befinden. Eventuell auch die Spielernamen; ist aber nicht so wichtig. Aktuell regel ich das über ein PHP-Query-Script, da ich dies als fertige Lösung gefunden habe. Quelle: ![]() Hier ein Link zum bereits konfigurierten Script: ![]() Jedoch möchte ich mit meinem Launcher nicht erst Text aus einer von PHP erstellten Seite auslesen und ihn dann anzeigen. Das kann ja auch direkt geschehen. Zum Thema Delphi & Query an Source Server habe ich schon einiges gefunden, jedoch stoße ich an einem Punkt an meine Grenzen. Es gibt ein Beispielprojekt, welches aufzeigt, wie ein Query an einen Source Server gesendet wird und empfangene Daten ausliest. Quelle: ![]() Dies Funktioniert auch ganz gut wenn man den Server und Port angibt. In meinem Fall wäre das IP:5.45.97.44 Port:2301 Wer sich das Projekt nicht erst laden möchte ums selber auszuprobieren... hier die Ausgabe:
Code:
Das ist auch erstmal ganz okay dass das so ausgegeben wird. Jedoch kann ich keine Ausgabe der aktuellen Anzahl der Spieler bzw. das Maximum der Spieler erkennen. Und ja, der Server war nicht Spielerlos.
ÿÿÿÿI[L-T-S] Epoch Origins (1.0.5.1/125548)Taviarma2arrowpcDayZ Epoch OriginsŠ„dw1.63.125548±þ `7'X@bt,r163,n125548,s7,i1,mf,lf,vf,dt,tcoop,g65545,c2147483647-2147483647,pw,Š„žPÿÿÿÿ…Nbº~PP3½Àü0óÿÿÿ¸ò#¸òf/D¸òÀü0óžÂBÀü0ójñCz'ÀüPÄñ3½Àü„óÿÿÿó#óf/DóÀü„óžÂBÀü„ójñCz'Àü,òجq¼!8x7P7wqbj¹u<ò–q¦'ƒHòb¹u0Àu„ò4k¹uk¹u²aº~¦'ƒ ”Ž/}¹u€òëUÑv³' ”Žâ`¹uht¹u³'; óðû)sP³' ”Žÿû)soåîYøò².DÀüz'Àüz'óùJA°Y<Ba<BÀüÜ;&DX©§0wh€îþîþ
ŒèÀü@ó"¶A$ôZ<+w œ±0w$ôÎW-wÓ<+w§È¹w6ŒPŒz'|óPŒÐtŒäóm¹uPŒ(óº pÿÿÿÿPØû˜ŸîYäõ˜ŸŒŠ¦0wh€îþîþŒP`(ØûõŒè¬0wЪ8+w“ɹwŒŒP˜ŸP3½³'PŒŒPŒ(jñC¬ô5wŒ(àŠ5w˜Ÿ€ŒŒPŒ„ö(õqeW0ôÐW3whõ4õ5wŒŒê¸*wŒŒê¸*wxõ°5w8Œ”5wûɹwŒ Weiterführende Informationen sollte ich hier bekommen: Quelle: ![]() Das einzige was ich noch verstehe ist, dass der Header & das Paket von A2S_Info im Delphi Quellcode angegeben wurde. Laut der Informationen auf der Valve Seite sollte doch auch die Anzahl der Spieler mit ausgegeben werden. :cry: Nun gut. ich poste nochmal den Code der das Paket senden und Empfängt:
Delphi-Quellcode:
Beim Kompilieren kommt bei mir die Meldung:
procedure TForm4.Button1Click(Sender: TObject);
var x: array[0..25] of byte; y: array[0..1248] of byte; i,ctr,r,p: integer; servername:string; currentmap:string; begin memo1.Clear; udp1.RemotePort:= edit2.Text; udp1.RemoteHost:= edit1.Text; udp1.Active:= true; // The following bytes represent the query 'TSource Engine Query' // This can be applicable to HL1 too. x[0]:= $FF; x[1]:= $FF; x[2]:= $FF; x[3]:= $FF; x[4]:= $54; x[5]:= $53; x[6]:= $6F; x[7]:= $75; x[8]:= $72; x[9]:= $63; x[10]:= $65; x[11]:= $20; x[12]:= $45; x[13]:= $6E; x[14]:= $67; x[15]:= $69; x[16]:= $6E; x[17]:= $65; x[18]:= $20; x[19]:= $51; x[20]:= $75; x[21]:= $65; x[22]:= $72; x[23]:= $79; x[24]:= $00; udp1.SendBuf(x,sizeof(x)); i:= 0; ctr:= 0; r:= 0; udp1.ReceiveBuf(y,sizeof(y)); repeat begin if y[i] = $00 then begin inc(ctr); if ctr = 1 then r:= i; if ctr = 2 then begin p:= i; servername:= copy(memo1.Text,r+1,(i-r)-1); lblservername.Caption:= servername; end; if ctr = 3 then begin currentmap:= copy(memo1.Text,p,(i-p)-1); lblcurrentmap.Caption:= currentmap; end; end; memo1.Text:= memo1.Text + chr(y[i]); inc(i); end; until i = 1248; end; Die Methode udp1Receive, auf die udp1.OnReceive verweist, enthält eine inkompatible Parameterliste. Soll der Verweis entfernt werden? Dort kann NEIN geklickt werden und das Programm tut, was es soll. Andere Quellen verwiesen auch auf die INDY9/10 Komponenten die man verwenden kann. Ich bin verwirrt wie ich weiter machen kann und würde mich sehr freuen wenn sich ein kundiger der Materie hier melden würde um mir zu helfen. Danke im Voraus. Florian |
AW: Query an Gameserver
Was hast du denn für eine Ausgabe erwartet?
Zitat:
Stell dir vor, die senden dir als Wert 42 um mit auszudrücken, dass der Server läuft und auch ansonsten alle Fragen damit zu beantworten. Diese Information - was bedeutet 42 - muss du wissen, sonst ist es Essig mit der Information. Also hole dir die Beschreibung zu dieser Bytefolge, damit du interpretieren kannst, was dir diese Bytes sagen wollen. |
AW: Query an Gameserver
Zitat:
Und poste noch die Deklaration der Methode udp1Receive welche dem OnReceive zugewiesen wurde. |
AW: Query an Gameserver
Zitat:
Ich verstehe zwar was du mir sagen möchtest mit dem entschlüsseln. Soll ich jetzt diese kryptischen Zeichen umwandeln? Warum gibt er die ersten Informationen in Klartext aus, den Rest aber nicht? Im PHP-Script wirds ja dann auch irgendwo entschlüsselt, nur finde ich nicht wo. Zitat:
Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PWideChar; var DataLen: Integer);
procedure TForm4.udp1Receive(Sender: TObject; Buf: PWideChar; var DataLen: Integer); begin packlen:= datalen; end; |
AW: Query an Gameserver
Zitat:
Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
Und dann schau Dir die erhaltenen Daten noch mals an. |
AW: Query an Gameserver
Zitat:
Das einzige Problem was es hier gibt ist, dass der Server Binärdaten sendet und Flodding reinen Text erwartet. Flodding: Da wird nirgendwo eine "8" stehen sondern eher sowas wie "◘". Außerdem ist da nichts verschlüsselt sondern es sind halt einfach nur Binärdaten und kein String... |
AW: Query an Gameserver
Zitat:
Ach Du arbeitest mit Delphi2010, andere aber nicht. Unter Delphi2007 zum Beispiel ist dort PAnsiChar und nicht PWideChar. Wenn ich dann die Funktionsdeklaration in PWideChar änderen, dann kommt exakt die Fehlermeldung wie beim TE. Und nun schau einmal mit was der TE arbeitet. |
AW: Query an Gameserver
Zitat:
Du hast Recht. |
AW: Query an Gameserver
Zitat:
Ich erwarte keinen Klartext... das hab ich vorher auch nicht erwähnt ... ich habe nur gefragt, warum der erste Teil als Klartext ankommt, der Rest aber nicht. Ich habe hier mit der Bitte um Hilfe geschrieben. Wie funktioniert das mit den Birnärdaten? wie wandele ich das in Klartext um ? |
AW: Query an Gameserver
Zum besseren Verständnis mal ein Beispiel:
Du fragst eine API von mir ab und bekommst als Rückgabe ein Byte mit dem Dezimalwert 42. Was bedeutet das? Erstmal gar nichts. In einem Text-Control dargestellt siehst du ein
Delphi-Quellcode:
.
*
Ohne weiteres Wissen über den Kontext weisst du nur, dass ich dir ein Byte geliefert habe, was nun alles mögliche bedeuten kann. Es kann tatsächlich bedeuten, dass es sich um ein Zeichen handelt und dann wäre es eben
Delphi-Quellcode:
.
*
Es kann bedeuten, dass die Motoren 1,3,5 laufen und die Motoren 0,2,4,6,7 nicht laufen
Delphi-Quellcode:
.
00101010
Es kann bedeuten dass die beiden Lampen A und B mit der Helligkeit 66% und 13% leuchten sollen
Code:
Die wirkliche Bedeutung bekommt man ausschließlich über den Kontext und den erfährt man vom Erzeuger dieser Daten, denn der legt diesen Kontext fest.
42d => 2Ah
A -> Ah => 10d / 15d = 66.66% B -> 2h => 2d / 15d = 13.33% |
AW: Query an Gameserver
Okay also aus deinem Post verstehe ich denn jetzt, dass ich das nicht hinbekommen kann, weil ich den Kontext nicht habe oder verstehe?!
Warum kannst du denn nicht so schreiben, dass man das auch als nicht Philosoph oder professioneller Programmierer verstehen kann? Das würde vieles leichter machen aktuell... Und bevor du es falsch verstehst... Ich erwarte nicht von dir, dass du mir einen Codeschnipsel hinschmeisst an dem ich dann rumkauen kann... |
AW: Query an Gameserver
Zitat:
Hast Du den Quellcode des Scriptes? Wenn ja poste in einmal, denn dort müsste ja die Dekodierungsroutine für die RawDaten enthalten sein. Zitat:
|
AW: Query an Gameserver
Zitat:
![]() |
AW: Query an Gameserver
Da diese Daten ohne Kontext eben rein philosophisch sind, welche Antwort hast du dann erwartet? Der Begriff Kontext sollte allerdings auch Nicht-Philosophen und Nicht-Programmieren bekannt sein.
Man kann es so nicht beantworten und einen Codeschnipsel kann es dann logischerweise auch nicht geben. Zitat:
|
AW: Query an Gameserver
Zitat:
Bei mir funktionieren beide Links... Der erste Link führt zum Quellcode des PHP-Scriptes, der 2te Link führt zu dem jetzt aktuell benutzten fertig konfigurierten PHP-Script welches alle Informationen anzeigt. Zum Quellcode des PHP-Scriptes:
Code:
Offensichtlich werden hier die Bytes für das PHP-Script definiert und ausgewertet, entschlüsselt und oder decodiert ...
<?php
/** * Class written by xPaw * * Website: http://xpaw.me * GitHub: https://github.com/xPaw/PHP-Source-Query-Class * * Special thanks to koraktor for his awesome Steam Condenser class, * I used it as a reference at some points. */ if( !defined( '__DIR__' ) ) // PHP < 5.3 { define( '__DIR__', dirname( __FILE__ ) ); } require __DIR__ . '/Exceptions.class.php'; require __DIR__ . '/Buffer.class.php'; require __DIR__ . '/Socket.class.php'; require __DIR__ . '/SourceRcon.class.php'; require __DIR__ . '/GoldSourceRcon.class.php'; use xPaw\SourceQuery\Exception\InvalidArgumentException; use xPaw\SourceQuery\Exception\TimeoutException; use xPaw\SourceQuery\Exception\InvalidPacketException; class SourceQuery { /** * Values returned by GetChallenge() * * TODO: Get rid of this? Improve? Do something else? */ const GETCHALLENGE_FAILED = 0; const GETCHALLENGE_ALL_CLEAR = 1; const GETCHALLENGE_CONTAINS_ANSWER = 2; /** * Engines */ const GOLDSOURCE = 0; const SOURCE = 1; /** * Packets sent */ const A2S_PING = 0x69; const A2S_INFO = 0x54; const A2S_PLAYER = 0x55; const A2S_RULES = 0x56; const A2S_SERVERQUERY_GETCHALLENGE = 0x57; /** * Packets received */ const S2A_PING = 0x6A; const S2A_CHALLENGE = 0x41; const S2A_INFO = 0x49; const S2A_INFO_OLD = 0x6D; // Old GoldSource, HLTV uses it const S2A_PLAYER = 0x44; const S2A_RULES = 0x45; const S2A_RCON = 0x6C; /** * Source rcon sent */ const SERVERDATA_EXECCOMMAND = 2; const SERVERDATA_AUTH = 3; /** * Source rcon received */ const SERVERDATA_RESPONSE_VALUE = 0; const SERVERDATA_AUTH_RESPONSE = 2; /** * Points to rcon class * * @var SourceQueryRcon */ private $Rcon; /** * Points to buffer class * * @var SourceQueryBuffer */ private $Buffer; /** * Points to socket class * * @var SourceQuerySocket */ private $Socket; /** * True if connection is open, false if not * * @var bool */ private $Connected; /** * Contains challenge * * @var string */ private $Challenge; /** * Use old method for getting challenge number * * @var bool */ private $UseOldGetChallengeMethod; public function __construct( ) { $this->Buffer = new SourceQueryBuffer( ); $this->Socket = new SourceQuerySocket( $this->Buffer ); } public function __destruct( ) { $this->Disconnect( ); } /** * Opens connection to server * * @param string $Ip Server ip * @param int $Port Server port * @param int $Timeout Timeout period * @param int $Engine Engine the server runs on (goldsource, source) * * @throws InvalidArgumentException * @throws TimeoutException */ public function Connect( $Ip, $Port, $Timeout = 3, $Engine = self :: SOURCE ) { $this->Disconnect( ); if( !is_int( $Timeout ) || $Timeout < 0 ) { throw new InvalidArgumentException( 'Timeout must be an integer.', InvalidArgumentException::TIMEOUT_NOT_INTEGER ); } if( !$this->Socket->Open( $Ip, (int)$Port, $Timeout, (int)$Engine ) ) { throw new TimeoutException( 'Could not connect to server.', TimeoutException::TIMEOUT_CONNECT ); } $this->Connected = true; } /** * Forces GetChallenge to use old method for challenge retrieval because some games use outdated protocol (e.g Starbound) * * @param bool $Value Set to true to force old method * * @returns bool Previous value */ public function SetUseOldGetChallengeMethod( $Value ) { $Previous = $this->UseOldGetChallengeMethod; $this->UseOldGetChallengeMethod = $Value === true; return $Previous; } /** * Closes all open connections */ public function Disconnect( ) { $this->Connected = false; $this->Challenge = 0; $this->Buffer->Reset( ); $this->Socket->Close( ); if( $this->Rcon ) { $this->Rcon->Close( ); $this->Rcon = null; } } /** * Sends ping packet to the server * NOTE: This may not work on some games (TF2 for example) * * @return bool True on success, false on failure */ public function Ping( ) { if( !$this->Connected ) { return false; } $this->Socket->Write( self :: A2S_PING ); $this->Socket->Read( ); return $this->Buffer->GetByte( ) === self :: S2A_PING; } /** * Get server information * * @throws InvalidPacketException * * @return bool|array Returns array with information on success, false on failure */ public function GetInfo( ) { if( !$this->Connected ) { return false; } $this->Socket->Write( self :: A2S_INFO, "Source Engine Query\0" ); $this->Socket->Read( ); $Type = $this->Buffer->GetByte( ); if( $Type === 0 ) { return false; } // Old GoldSource protocol, HLTV still uses it if( $Type === self :: S2A_INFO_OLD && $this->Socket->Engine === self :: GOLDSOURCE ) { /** * If we try to read data again, and we get the result with type S2A_INFO (0x49) * That means this server is running dproto, * Because it sends answer for both protocols */ $Server[ 'Address' ] = $this->Buffer->GetString( ); $Server[ 'HostName' ] = $this->Buffer->GetString( ); $Server[ 'Map' ] = $this->Buffer->GetString( ); $Server[ 'ModDir' ] = $this->Buffer->GetString( ); $Server[ 'ModDesc' ] = $this->Buffer->GetString( ); $Server[ 'Players' ] = $this->Buffer->GetByte( ); $Server[ 'MaxPlayers' ] = $this->Buffer->GetByte( ); $Server[ 'Protocol' ] = $this->Buffer->GetByte( ); $Server[ 'Dedicated' ] = Chr( $this->Buffer->GetByte( ) ); $Server[ 'Os' ] = Chr( $this->Buffer->GetByte( ) ); $Server[ 'Password' ] = $this->Buffer->GetByte( ) === 1; $Server[ 'IsMod' ] = $this->Buffer->GetByte( ) === 1; if( $Server[ 'IsMod' ] ) { $Mod[ 'Url' ] = $this->Buffer->GetString( ); $Mod[ 'Download' ] = $this->Buffer->GetString( ); $this->Buffer->Get( 1 ); // NULL byte $Mod[ 'Version' ] = $this->Buffer->GetLong( ); $Mod[ 'Size' ] = $this->Buffer->GetLong( ); $Mod[ 'ServerSide' ] = $this->Buffer->GetByte( ) === 1; $Mod[ 'CustomDLL' ] = $this->Buffer->GetByte( ) === 1; } $Server[ 'Secure' ] = $this->Buffer->GetByte( ) === 1; $Server[ 'Bots' ] = $this->Buffer->GetByte( ); if( isset( $Mod ) ) { $Server[ 'Mod' ] = $Mod; } return $Server; } if( $Type !== self :: S2A_INFO ) { throw new InvalidPacketException( 'GetInfo: Packet header mismatch. (0x' . DecHex( $Type ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); } $Server[ 'Protocol' ] = $this->Buffer->GetByte( ); $Server[ 'HostName' ] = $this->Buffer->GetString( ); $Server[ 'Map' ] = $this->Buffer->GetString( ); $Server[ 'ModDir' ] = $this->Buffer->GetString( ); $Server[ 'ModDesc' ] = $this->Buffer->GetString( ); $Server[ 'AppID' ] = $this->Buffer->GetShort( ); $Server[ 'Players' ] = $this->Buffer->GetByte( ); $Server[ 'MaxPlayers' ] = $this->Buffer->GetByte( ); $Server[ 'Bots' ] = $this->Buffer->GetByte( ); $Server[ 'Dedicated' ] = Chr( $this->Buffer->GetByte( ) ); $Server[ 'Os' ] = Chr( $this->Buffer->GetByte( ) ); $Server[ 'Password' ] = $this->Buffer->GetByte( ) === 1; $Server[ 'Secure' ] = $this->Buffer->GetByte( ) === 1; // The Ship (they violate query protocol spec by modifying the response) if( $Server[ 'AppID' ] === 2400 ) { $Server[ 'GameMode' ] = $this->Buffer->GetByte( ); $Server[ 'WitnessCount' ] = $this->Buffer->GetByte( ); $Server[ 'WitnessTime' ] = $this->Buffer->GetByte( ); } $Server[ 'Version' ] = $this->Buffer->GetString( ); // Extra Data Flags if( $this->Buffer->Remaining( ) > 0 ) { $Server[ 'ExtraDataFlags' ] = $Flags = $this->Buffer->GetByte( ); // The server's game port if( $Flags & 0x80 ) { $Server[ 'GamePort' ] = $this->Buffer->GetShort( ); } // The server's SteamID - does this serve any purpose? if( $Flags & 0x10 ) { $Server[ 'ServerID' ] = $this->Buffer->GetUnsignedLong( ) | ( $this->Buffer->GetUnsignedLong( ) << 32 ); // TODO: verify this } // The spectator port and then the spectator server name if( $Flags & 0x40 ) { $Server[ 'SpecPort' ] = $this->Buffer->GetShort( ); $Server[ 'SpecName' ] = $this->Buffer->GetString( ); } // The game tag data string for the server if( $Flags & 0x20 ) { $Server[ 'GameTags' ] = $this->Buffer->GetString( ); } // GameID -- alternative to AppID? if( $Flags & 0x01 ) { $Server[ 'GameID' ] = $this->Buffer->GetUnsignedLong( ) | ( $this->Buffer->GetUnsignedLong( ) << 32 ); } if( $this->Buffer->Remaining( ) > 0 ) { throw new InvalidPacketException( 'GetInfo: unread data? ' . $this->Buffer->Remaining( ) . ' bytes remaining in the buffer. Please report it to the library developer.', InvalidPacketException::BUFFER_NOT_EMPTY ); } } return $Server; } /** * Get players on the server * * @throws InvalidPacketException * * @return bool|array Returns array with players on success, false on failure */ public function GetPlayers( ) { if( !$this->Connected ) { return false; } switch( $this->GetChallenge( self :: A2S_PLAYER, self :: S2A_PLAYER ) ) { case self :: GETCHALLENGE_FAILED: { return false; } case self :: GETCHALLENGE_ALL_CLEAR: { $this->Socket->Write( self :: A2S_PLAYER, $this->Challenge ); $this->Socket->Read( 14000 ); // Moronic Arma 3 developers do not split their packets, so we have to read more data // This violates the protocol spec, and they probably should fix it: https://developer.valvesoftware.com/wiki/Server_queries#Protocol $Type = $this->Buffer->GetByte( ); if( $Type === 0 ) { return false; } else if( $Type !== self :: S2A_PLAYER ) { throw new InvalidPacketException( 'GetPlayers: Packet header mismatch. (0x' . DecHex( $Type ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); } break; } } $Players = Array( ); $Count = $this->Buffer->GetByte( ); while( $Count-- > 0 && $this->Buffer->Remaining( ) > 0 ) { $Player[ 'Id' ] = $this->Buffer->GetByte( ); // PlayerID, is it just always 0? $Player[ 'Name' ] = $this->Buffer->GetString( ); $Player[ 'Frags' ] = $this->Buffer->GetLong( ); $Player[ 'Time' ] = (int)$this->Buffer->GetFloat( ); $Player[ 'TimeF' ] = GMDate( ( $Player[ 'Time' ] > 3600 ? "H:i:s" : "i:s" ), $Player[ 'Time' ] ); $Players[ ] = $Player; } return $Players; } /** * Get rules (cvars) from the server * * @throws InvalidPacketException * * @return bool|array Returns array with rules on success, false on failure */ public function GetRules( ) { if( !$this->Connected ) { return false; } switch( $this->GetChallenge( self :: A2S_RULES, self :: S2A_RULES ) ) { case self :: GETCHALLENGE_FAILED: { return false; } case self :: GETCHALLENGE_ALL_CLEAR: { $this->Socket->Write( self :: A2S_RULES, $this->Challenge ); $this->Socket->Read( ); $Type = $this->Buffer->GetByte( ); if( $Type === 0 ) { return false; } else if( $Type !== self :: S2A_RULES ) { throw new InvalidPacketException( 'GetRules: Packet header mismatch. (0x' . DecHex( $Type ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); } break; } } $Rules = Array( ); $Count = $this->Buffer->GetShort( ); while( $Count-- > 0 && $this->Buffer->Remaining( ) > 0 ) { $Rule = $this->Buffer->GetString( ); $Value = $this->Buffer->GetString( ); if( !Empty( $Rule ) ) { $Rules[ $Rule ] = $Value; } } return $Rules; } /** * Get challenge (used for players/rules packets) * * @param $Header * @param $ExpectedResult * @throws InvalidPacketException * @return bool True if all went well, false if server uses old GoldSource protocol, and it already contains answer */ private function GetChallenge( $Header, $ExpectedResult ) { if( $this->Challenge ) { return self :: GETCHALLENGE_ALL_CLEAR; } if( $this->UseOldGetChallengeMethod ) { $Header = self :: A2S_SERVERQUERY_GETCHALLENGE; } $this->Socket->Write( $Header, 0xFFFFFFFF ); $this->Socket->Read( ); $Type = $this->Buffer->GetByte( ); switch( $Type ) { case self :: S2A_CHALLENGE: { $this->Challenge = $this->Buffer->Get( 4 ); return self :: GETCHALLENGE_ALL_CLEAR; } case $ExpectedResult: { // Goldsource (HLTV) return self :: GETCHALLENGE_CONTAINS_ANSWER; } case 0: { return self :: GETCHALLENGE_FAILED; } default: { throw new InvalidPacketException( 'GetChallenge: Packet header mismatch. (0x' . DecHex( $Type ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); } } } /** * Sets rcon password, for future use in Rcon() * * @param string $Password Rcon Password * * @return bool True on success, false on failure */ public function SetRconPassword( $Password ) { if( !$this->Connected ) { return false; } switch( $this->Socket->Engine ) { case SourceQuery :: GOLDSOURCE: { $this->Rcon = new SourceQueryGoldSourceRcon( $this->Buffer, $this->Socket ); break; } case SourceQuery :: SOURCE: { $this->Rcon = new SourceQuerySourceRcon( $this->Buffer, $this->Socket ); break; } } $this->Rcon->Open( ); return $this->Rcon->Authorize( $Password ); } /** * Sends a command to the server for execution. * * @param string $Command Command to execute * * @return string|bool Answer from server in string, false on failure */ public function Rcon( $Command ) { if( !$this->Connected ) { return false; } return $this->Rcon->Command( $Command ); } } |
AW: Query an Gameserver
Zitat:
Jetzt musst Du halt nur noch den PHP-Code in Delphi nachbilden. Also einfacher kann man es nun wirklich nicht haben. |
AW: Query an Gameserver
Code:
Hier werden die Spieler auf dem Server erkannt ja?
case self :: GETCHALLENGE_ALL_CLEAR:
{ $this->Socket->Write( self :: A2S_PLAYER, $this->Challenge ); $this->Socket->Read( 14000 ); $Type = $this->Buffer->GetByte( ); if( $Type === 0 ) { return false; } else if( $Type !== self :: S2A_PLAYER ) { throw new InvalidPacketException( 'GetPlayers: Packet header mismatch. (0x' . DecHex( $Type ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); } break; } Im oberen Teil des Codes finde ich :
Code:
Dies sollte bedeuten, dass der Buffer den ich senden muss so aussehen sollte:
const A2S_PLAYER = 0x55;
FF FF FF FF 55 Dabei fehlt mir dann aber dieses Challenge ... das versteh ich nicht was damit gemeint ist. :oops: |
AW: Query an Gameserver
Hast Du Dir diese Seite schon angesehen, welche Neutral General bereits gepostet hat?
![]() |
AW: Query an Gameserver
Ja und ich lese und lese und lese...
Ich habe das so verstanden: Insgesammt gibt es 5 verschiedene Arten dem Server Daten abzufragen... diese wären: A2S_INFO A2S_PLAYER A2S_RULES A2A_PING A2S_SERVERQUERY_GETCHALLENGE Um diese Daten abzufragen benötige ich eine UDP/IP Verbindung. Über diese Verbindung muss ich jedes mal den HEADER mitsenden... dieser besteht aus FF FF FF FF (nicht gesplittetes Paket) oder FF FF FF FE (gesplittetes Paket). Da die Größe der Daten die ich abfragen will gering ist, gehe ich davon aus, dass ich nur den ersten HEADER benutzen muss. Somit haben wir den ersten teil des Buffers schon zusammen. FF FF FF FF um jetzt die gewünschten Daten zu bekommen muss ich das benütigte Byte hinten dranhängen. |
AW: Query an Gameserver
Okay ... also ich habe jetzt verstanden, dass ich für die Spieler erst ein Challenge brauche...
dieses Challenge bekomme ich auch... FF FF FF FF 55 FF FF FF FF Damit bekomme ich dann folgende ausgabe im RAW format:
Code:
ÿÿÿÿA heisst ja nun dass es die antwort auf die Anfrage ist ein Challenge zu bekommen...
ÿÿÿÿA1º
Lðˆ' €x1€ÖvŸs'/à'øïŒ+Çvà'¤ 'ð½jÇvBòðFtÆvà'Bò`ð(BÇvBò 'ü' 'à''T'XðhiÇvà'xðŒÆvSzÆv* BòÄðeT¼vBòÄð².D$ñÿÿÿÿà'ÀðìðPñ$ñÈ pqçÈ ôðùJA°íY<Ba<BpqçÜê;&DñR&DZ&DÈñ¹r¹u»t¹u¶c…ûƒž¶cÿÿÿÿ…Îv¶c¶c*i¹uvr¹uWr¹u¶c©¶cpñ(zq¹uøÎv6q¹u(¶c¶c¶clñ¶c°ñŒj¹uðv¥…ûƒòdq¶c…ûƒûƒ€1/}¹u/}¹u4òجqJ4p;H;wqbj¹u(ò–qûƒ4òb¹u0Àuôpò4k¹uk¹uÎvûƒ€1/}¹ulòëUÑvYZ€1â`¹uht¹uYZ;óðû)s¶cYZ€1ÿû)sò°´FYZ€x1¶cЙ!sðv¥ðv¥ó,p¹uðv¥_¶cЙ!s&ëƒüÀ2©§0wÐ2Xîþîþ p2/(2Й!sò° $ôZ<+w œ±0w$ôÎW-wÓ<+w©±Mu6/P/Í«ºÜP/¨t/¤ó èÖvP/h2”ÎvЙ!s $¸2Øû0pw‘m¹uxpw/Ц0wÐ2Xîþîþ/p2 ¶cÈ2h2Øû0h õ/è¬0w€2¨ª8+w°Mu/x2/”/Dpw¶c…ò°P//P/h2žÂB€å¬ô5w/h2¸Š5wpw€//P/x2® h2õqeW0ôÐW3whõ4õ5w//p2ê¸*wp2//ê¸*wp2xõ°5w8/”5wõ°Mu/// g§ g Jetzt ist mein "neues" Problem, dass ich nicht weiss wie ich die nächsten Query senden soll... FF FF FF FF 55 :?CHALLENGE im RAW Format?!:? |
AW: Query an Gameserver
Okay habs jetzt hinbekommen, dass ich in der RAW data meine Spielernamen sehe...
jetzt hänge ich an dem Punkt die Spielernamen einzeln auszulesen und zu zählen... hier der php-code der umgewandelt werden muss...
Code:
Ich weiss leider nicht, wie ich in Delphi aus dem Byte was zurück kommt nur die Strings auslese...
$Players = Array( );
$Count = $this->Buffer->GetByte( ); while( $Count-- > 0 && $this->Buffer->Remaining( ) > 0 ) { $Player[ 'Id' ] = $this->Buffer->GetByte( ); // PlayerID, is it just always 0? $Player[ 'Name' ] = $this->Buffer->GetString( ); $Player[ 'Frags' ] = $this->Buffer->GetLong( ); $Player[ 'Time' ] = (int)$this->Buffer->GetFloat( ); $Player[ 'TimeF' ] = GMDate( ( $Player[ 'Time' ] > 3600 ? "H:i:s" : "i:s" ), $Player[ 'Time' ] ); $Players[ ] = $Player; } return $Players; |
AW: Query an Gameserver
Im Prinzip ist es ganz einfach (wenn man sich die Daten genau anschaut).
Dann fällt auf, dass jeder String mit einem 00 Byte abgeschlossen ist. Wie ich denn darauf komme? A2S_INFO
Code:
Schauen wir uns das Datenpaket an, dann finden wir die ersten 4 Bytes mit
FF FF FF FF 54 53 6F 75 72 63 65 20 45 6E 67 69 ÿÿÿÿTSource Engi
6E 65 20 51 75 65 72 79 00 ne Query
Delphi-Quellcode:
, dann den Header mit
FF
Delphi-Quellcode:
und der Rest ist der String ... öh, bis auf das letzte Byte
54
Delphi-Quellcode:
.
00
Nächstes Beispiel angesehen
Code:
Aha, da sind ja wieder diese
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.
Delphi-Quellcode:
Bytes zwischen den einzelnen Strings. Sehr schön.
00
Um dieses jetzt vernünftig zu lösen, baut man sich eine Klasse, die aus einem Stream diese Daten-Formate auslesen kann (so wie im PHP-Skript). |
AW: Query an Gameserver
Habs jetzt per Zufall irgendwie hinbekommen...
ich lass es nicht mehr in das memo schreiben. Anstelledessen wandele ich "irgendetwas" von InttoStr und lass es als label anzeigen... anzahl der Spieler funktioniert so... Aber ich weiss nicht warum :?
Delphi-Quellcode:
Das geschieht gleich nachdem ich das Paket mit dem Challengekey gesendet habe.
udp1.ReceiveBuf(y,sizeof(y));
players := IntToStr(y[5]); label7.Caption := players; |
AW: Query an Gameserver
Wenn y ein Array of Byte/Integer.. ist, dann sollte das immer funktionieren. Ob das Ergebnis immer richtig ist (im Sinne der erwarteten Information), steht auf einem anderen Blatt.
Gruß K-H P.S. Zitat:
|
AW: Query an Gameserver
Okay also ich habe mich entschieden einen anderen Ansatz zu verfolgen...
dass mir gestern die Aktuelle Spielerzahl gezeigt wurde, bzw. immernoch gezeigt wird, liegt scheinbar dadran, dass ich das BYTE direkt auslesen kann weil es noch eine kleine Zahl ist. Wird die Spielerzahl größer, denke ich dass ich dann anders auslesen muss... Also ich habe es jetzt hinbekommen, mir den HEX Code anzeigen zu lassen. Wandele ich diesen in String um, habe ich wieder meine kryptischen Zeichen. Offenbar bin ich auf dem richtigen weg, denn JETZT ist die ausgabe der Antwort gleich der Beispiele von Sir Rufo...
Code:
Da haben wir die besagten doppel 00en von denen vorher gesprochen wurde.
FFFFFFFF49115B4C2D542D535D2045706F6368204F726967696E732028312E302E3
52E312F3132353534382900546176690061726D61326172726F777063004461795A 2045706F6368204F726967696E73008A8401190064770000312E36332E313235353 43800B1FE08099448375C14400162742C723136332C6E3132353534382C73372C69 312C6D662C6C662C76662C64742C74636F6F702C6736353534352C6332313437343 8333634372D323134373438333634372C70772C008A8400000000000000000000C4 F01800265D3277F0F4180047444546A08A40034003000000000000F4F018009CF21 80040030000F8F4180000F1180064FB33770000010030374103EE0B000000000000 CCF118002CF218008BF23377CCF118000CF2180000F218000000000000000000A08 A400334F11800EBAE3377E08D4003A08A40032CF21800F4F11800E8F1180000F218 000CF21800244A4003000000000000000048F11800C55E3277F0F41800010000000 0000100BD7B3377F0F41800000000000100010033BD0000C015370030F31800FFFF FF00B8F2180010233F00B8F21800662F4400B8F2180000000000C015370030F3180 09EC24200C015370030F318006AF14300000000007A0F2600C015370000000000FF FFFFFF33BD0000C015370084F31800FFFFFF000CF3180010233F000CF31800662F4 4000CF3180000000000C015370084F318009EC24200C015370084F318006AF14300 000000007A0F2600C0153700184840030200320032001800C810400304003200320 018003412400308003200320018009CF21800010000015400180000000100B8132F 77244A40030C0003000300000000003200341240030000000032000000C81040030 1000000F8F41800F0F4180000000000288E40033C00000000000000010001000000 0000F8F41800F0F418001848400332000000F0F418008848400332000000F0F4180 0244A4003030000000300000080F11800000001006C61746E64666C740000000000 080B000B000000F8F21800B22E4400C01537000000000000000000000000007A0F2 60000000000C01537007A0F260000F31800F94A4100B01C3F00593C4200613C4200 C015370090DC3C003B264400A8988F00A9A79877B8988F00B8010000EEFEEEFE0A0 0000058988F0000008E00709A8F00C015370040F3180022B6410000000000390000 0024F418005A3C93770A0000009CB1987724F41800CE579577D33C93777B35EB773 60000000000000000008E0050018E007A0F26007CF3180050018E00080200007C01 8E00E4F3180039000000046DC07650018E0043000000C03C9000F3BA00004300000 0010000000A0000000000000070000000FFFFFFFFA0988F000000000002000406E0 F88F004902044F00000000E01210C3000000007F000000E4F518006502046300008 E008AA69877B8988F00B8010000EEFEEEFE00008E0058988F00000000003900043D 20067D00B0988F00A0FD8F00E0F88F00C80100003900000010F5180000008E00E8A C987768988F0008020000AA3893774F34EB770000000000008E0060988F0000008E 00010000000200040620067D006502046320067D0033BD00008C08011150018E000 0008E000000000050018E00A0FD8F006AF1430000000000ACF4180006089D770000 8E00000000004300000004000000010000000400000000000000C03C90000000000 0180200007F0000008A109D774902044F4300000080018E00000000000000000000 008E0050018E000000000060988F0000000001A0FD8F004300000000000000F5710 00100000000430000006557000030F41800D0579B7768F5180034F5180006089D77 00008E000000000000008E0058988F00EAB8927758988F0000008E0000008E00EAB 8927758988F0078F51800B0179D7738018E0094179D772734EB77 Im oberen Bereich noch ganz annehmbar und überschaubar, reihen sie sich nach unten hin immer mehr aneinander und ich schaffs einfach nicht das sinnvoll auszulesen und dann von BYTE in STRING bzw. INTEGER umzuwandeln. Dazu kommt, dass offenbar mehrere HEADER mitgesendet wurden und mir das dann das Auslesen erschwert, weil bei einer Schleife, die ein Array füllt dann immer wieder der HEADER dazwischen stehen würde. Desweiteren weiss ich nicht wie ich ein Array mit den Daten füllen soll :? Sollte es ein Array mit den BYTES sein ? |
AW: Query an Gameserver
Nein, kein Array. Du bekommst strukturierte Daten, und in dieser Struktur kommen mehrere Typen gemischt vor. Deine ganze Umwandlerei in Strings schmeiss doch einfach mal bitte komplett weg, das führt zu nichts, weil du eben nicht einen String zurück gesendet bekommst. Es ist schlicht die völlig falsche Interpretation der Daten.
Wie die Daten zu interpretieren sind, wird in dem Link zu der Steam Seite haarklein aufgelistet, die weiter vorne hier verlinkt wurde. Da stehen ausführlich ALLE Informationen, die man braucht um die Antwortpakete richtig zu interpretieren. Und wenn alle Stricke reissen, kannst du den fix und fertigen PHP code, den du selbst geposted hast, nach Delphi übersetzen und aus der damit interpretierten Struktur die für dich interessanten Werte nutzen. Komfortabler geht es einfach nicht mehr. Es ist alles da an Infos, sogar eine Beispielimplementierung. Du musst einfach nur mal von dem Trip runter kommen, dass da Strings kommen. Ist nicht so. Das ist keine XML Datei. Komische Zeichen bekommst du nur, weil du die Daten komplett falsch interpretierst. Ja, es sind Abschnitte drin, die einzelne Strings ergeben. Aber das ist halt nicht für alles so. Mache dich am besten erstmal mit einer Hand voll Grundlagen zu Datentypen und der internen Darstellung von Ganzzahlen, Texten und Gleitpunktzahlen vertraut. Das ist absolut elementares Wissen, dass du beim Programmieren praktisch jede Minute brauchst. Es ist also nichtmals so, dass das gelernte so speziell ist, dass es nur auf dein aktuelles Problem zutrifft. Das krasse Gegenteil ist der Fall. |
AW: Query an Gameserver
Hallo nochmal.
Also ich hab jetzt jede Menge ausprobiert und wollte auch anfangen den Code von PHP nach DELPHI zu portieren. Daran scheitere ich jedoch komplett, da ich den PHP-Code nicht 100% verstehe. Des Weiteren ist was von Kompression aufgekreuzt... In diesem Script wird geschaut ob der empfangene Buffer "compressed" ist oder nicht. ![]() Ich weiss nicht wie ich damit umgehen soll. Ich hab nur rausgefunden, dass es scheinbar eine ZLIB Kompression ist. Weiterhin bin ich soweit, dass ich jetzt die ersten 6 empfangenen Informationen sicher empfangen kann. Das wären dann: Header, Protocol, Name, Map, Folder, Game Ab der 6ten bzw. 7ten Information (Map ja oder nein) hapert es jedoch, denn dort steht eine doppel"00" dafür, dass keine Spieler auf dem Server sind und dann erkennt mein Programm aber nur die "00" als Ende der Information und überspringt diese. hier einmal die empfangenen Daten: Mit Map:
Code:
jetzt das ganze mit Spieler und Map
49; Header
11; Protokol 5B4C2D542D535D2045706F6368204F726967696E732028312E302E352E312F3132353534382900; Name 5461766900; Map 61726D61326172726F77706300; Folder 4461795A2045706F6368204F726967696E7300; Game 8A8400; Diese beiden Zeilen sollten eigentlich 3300; Eine Zeile sein. 00 Problem. Sie beinhalten die ID (33930), Anzahl Spieler und Max Spieler 647700; Server Type / Environment 00; ?? Bots ?? Kann ich leider nicht testen, da in diesem Spiel Bots nicht erkannt werden. 312E36332E31323535343800; Spielversion B1FE08031027D86514400162742C723136332C6E3132353534382C73372C69312C6D662C6C662C76662C64742C74636F6F702C6736353534352C63323134373438333634372D323134373438333634372C70772C00; GameTags 8A8400; Game ID , sollte umgewandelt "33930" ergeben. tut es auch nicht. 00; 00; 00; 00; 00;
Code:
Die Unterschiede konnte ich leider nicht FETT markieren, habe sie aber kommentiert.
49;
11; 5B4C2D542D535D2045706F6368204F726967696E732028312E302E352E312F3132353534382900; 5461766900; 61726D61326172726F77706300; 4461795A2045706F6368204F726967696E7300; 8A84013300; Hier wurde Jetzt nicht getrennt, weil "00" zu "01" wude 64770100; Hier ist noch Password hinzugekommen. (getestet) 312E36332E31323535343800; B1FE0801B0DD6A6614400162742C723136332C6E3132353534382C73332C69312C6D662C6C662C76662C64742C74636F6F702C6736353534352C63323134373438333634372D323134373438333634372C70772C00; 8A8400; 00; 00; 00; 00; 00; Also das Sortieren der Bytes anhand der "00"en schlägt in diesem Fall fehl. Jetzt würde ich gerne wissen was ICH falsch mache oder wie ich es besser machen kann... Anbei nochmal der Quellcode mit dem ich die Informationen bekomme.
Delphi-Quellcode:
MFG
procedure TForm4.Button1Click(Sender: TObject);
var A: TStringList; x: array[0..25] of byte; y: array[0..1400] of byte; i: integer; buff_delemitted: string; hexzahl: string; dezimalzahl: integer; server_protocol: string; map_name: string; server_name: string; folder_name: string; game_name: string; game_id: string; server_type: string; server_environment: string; max_players: string; begin memo1.Clear; udp1.RemotePort:= '5.45.97.44'; udp1.RemoteHost:= '2302'; udp1.Active:= true; x[0]:= $FF; x[1]:= $FF; x[2]:= $FF; x[3]:= $FF; x[4]:= $54; x[5]:= $53; x[6]:= $6F; x[7]:= $75; x[8]:= $72; x[9]:= $63; x[10]:= $65; x[11]:= $20; x[12]:= $45; x[13]:= $6E; x[14]:= $67; x[15]:= $69; x[16]:= $6E; x[17]:= $65; x[18]:= $20; x[19]:= $51; x[20]:= $75; x[21]:= $65; x[22]:= $72; x[23]:= $79; x[24]:= $00; udp1.SendBuf(x,sizeof(x)); udp1.ReceiveBuf(y,sizeof(y)); for i := 4 to sizeof(y) - 1 do begin if (IntToHex(y[i], 2) = '49') then begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';' end else if i = 5 then begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';' end else if (IntToHex(y[i], 2) = '00') then begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';' end else begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) end; Application.ProcessMessages; end; Memo1.Text := buff_delemitted; A := TStringList.Create; try Split(';', buff_delemitted, A) ; server_protocol := inttostr(hextoint(a[1])); server_name := HexStrToString(a[2]); map_name := HexStrToString(a[3]); folder_name := HexStrToString(a[4]); game_name := HexStrToString(a[5]); server_type := HexStrToString(a[7])[1]; server_environment := HexStrToString(a[7])[2]; max_players := HexStrToString(a[8]); finally A.Free; end; end; |
AW: Query an Gameserver
Du willst dir anscheinend nicht die Dokumentation von Valve durchlesen. Dass ist natürlich fatal und daher gebe ich dir nur noch einmal den Rat, dieses dringend nachzuholen.
Ganz oben steht da nämlich die Definition der Data Types
Wenn ich dann lese, dass du die ID (Steam Application ID of game) da vermutest
Code:
dann hast du die Dokumentation nämlich nicht gelesen.
8A8400; Diese beiden Zeilen sollten eigentlich
3300; Eine Zeile sein. 00 Problem. Sie beinhalten die ID (33930), Anzahl Spieler und Max Spieler Wenn ich jetzt mal deine Daten dem Response Format laut Dokumentation zuordne, dann erhalte ich
|
AW: Query an Gameserver
Also ist meine Angehensweise total falsch.
Ich muss Die Reihenfolge der Empfangenen Daten beachten um dann gezielt die Bytes auszulesen. Ich habe ehrlich gesagt die ganze Zeit nicht die Längenangaben hintendran beachtet und somit bin ich von völlig falschen längen ausgegangen. Theoretisch müsste ich in etwa so interpretieren: Header: Lese 1tes Byte und wandele um, Protocol: Lese 2tes Byte und wandele um, Name: Fang an zu lesen nach 2tem Byte und stoppe bei erstem "00" , merke dir wo du aufgehört hast zu lesen!!! Map: Lese weiter wo du vorher aufgehört hast und weiter bis zum nächsten "00"; merk dir wieder die Endposition. Folder: weiter "00" bis "00" lesen, merken, Game: weiter "00" bis "00" lesen, merken, ID: Lese von der LETZTEN Position 2 Bytes weiter und wandele um, merk dir neue Position, Players: Lese 1 weiteres Byte, wandele um und merk dir Position, Max. Players: ....... und so weiter. Korrekt? EDIT: Die ID : 8A84 sollte umgewandelt 33930 ergeben. Tut es aber nicht egal in was ich umwandel. HextoStr gibt Kryptisches, HextoInt gibt eine falsche Zahl: 35460. |
AW: Query an Gameserver
Zitat:
Na, fällt Dir etwas auf? |
AW: Query an Gameserver
ja das habe ich natürlich probiert...
von SIGNED INT to HEX funktionierts ja auch. aber von HEX to SIGNED INT häng ich. ich bekomms nur in INT gewandelt und das gibt dann den falschen wert. |
AW: Query an Gameserver
Es ist erheblich einfacher als du denkst, aber du musst eben etwas umdenken :)
Zunächst einmal schaust du dir die Datentypen an, die Valve da vorgibt und suchst dir die passenden Delphi-Typen dazu heraus ![]() Dann könnte man das hier bauen
Delphi-Quellcode:
Jetzt gibt es in Delphi auch so nette Sachen wie
type
TValveByte = Byte; {8 bit character or unsigned integer} TValveShort = SmallInt; {16 bit signed integer} TValveLong = Integer; {32 bit signed integer} TValveFloat = Single; {32 bit floating point} TValveLongLong = Cardinal; {64 bit unsigned integer} ![]() ![]() ![]() Du bekommst ja eine Handvoll Bytes und die kann man in einen ![]() Hier mal ein kleines Beispiel zum Starten:
Delphi-Quellcode:
Wie du sehen kannst, vermeide ich auf Teufel komm raus, irgendwo im Quelltext mit Magic Values zu arbeiten.
const
SIMPLE_RESPONSE_HEADER : TValveLong = $FFFFFFFF; MULTIPACKET_RESPONSE_HEADER : TValveLong = $FFFFFFFE; procedure Foo( AResponse : TBytes ); var LResponseStream : TStream; LHeader : TValveLong; begin LResponseStream := TBytesStream.Create( AResponse ); try LResponseStream.ReadData( LHeader ); // Wir lesen den 4-Byte Header case LHeader of SIMPLE_RESPONSE_HEADER : WorkOnSimpleResponse( LResponseStream ); MULTIPACKET_RESPONSE_HEADER : WorkOnMultiPacketResponse( LResponseStream ); else raise Exception.CreateFmt( 'Unknown Header Value %8.8x', [LHeader] ); end; finally LResponseStream.Free; end; end; Wenn irgendein Wert eine bestimmte Bedeutung hat, dann definiert man sich einen Konstante und verwendet diese Konstante im Quelltext. Dann bleibt das auch immer schön lesbar. |
AW: Query an Gameserver
Bist du dir auch sicher, dass
Delphi-Quellcode:
auch wirklich
[$8A, $84]
Delphi-Quellcode:
bedeuten oder evtl. eher
$8A84
Delphi-Quellcode:
?
$848A
In der Valve-Dokumentation wird ganz eindeutig von All types are little endian gesprochen, was wiederum bedeutet, dass du dir diese Seite anschauen solltest ![]() und dann bedeutet diese Bytefolge
Delphi-Quellcode:
eben genau
[$8A, $84]
Delphi-Quellcode:
entspricht was dezimal dann tatsächlich
$848A
Delphi-Quellcode:
entspricht. Nur im Speicher sind die Bytes eben in umgekehrter Reihenfolge abgelegt.
33930
Darauf achten musst du nicht, wenn du diese Werte (wie in meinem Beitrag zuvor gezeigt) direkt aus dem Stream liest (lese einfach eine
Delphi-Quellcode:
, bzw.
SmallInt
Delphi-Quellcode:
)
Int16
|
AW: Query an Gameserver
Zitat:
Zitat:
|
AW: Query an Gameserver
Zitat:
Aber ich muss dazu sagen, dass es kaum möglich ist, diskreter darauf hinzuweisen. Man schaue sich "8A84" und "848A" nur mal an. Ich möchte nicht unhöflich klingen, aber man hätte mich einfach direkt drauf hinweisen können OHNE zu warten und sich dann lustig drüber zu machen. Ich bin sehr dankbar für die Hilfe die mir hier angeboten wird und interpretiere sie auch nicht als lästiges Rumgedruckse oder Ähnliches. Indirekt / eloquent jemanden zu beleidigen ist trotzdem nicht die feine Art und ich bitte dies in Zukunft zu unterlassen. Sicher hätte ich dem preventieren können wenn ich wie der Fuchs aufgepasst hätte. Wir stempeln das als meinen Fehler ab :thumb: Soviel zum Offtopic! Delphi kennt TBytes bei mir nicht.
Delphi-Quellcode:
Des Weiteren verstehe ich am Beispiel noch nicht so ganz, wie ich unbekannten Inhalt erkennen soll. Das mit dem Header war ja nun leicht weil er bekannt ist.
procedure Foo( AResponse : TBytes );
|
AW: Query an Gameserver
Zitat:
Man sieht sofort den Unterschied, da es in der Muttersprache geschrieben steht. "8A84" und "848A" Man sieht sofort den Unterschied, wenn man sich mit der Sprache beschäftigt, also hier sich die hexadezimale Schreibweise zu Eigen gemacht hat. Und diese Schreibweise kommt in der Computerwelt an jeder Stelle um die Ecke. Zitat:
Wir können Dir auch das Programm komplett schreiben, aber wo wäre da der Lerneffekt. Wenn Du also das nächste mal mit einer Antwort etwas nicht anfangen kannst, dann frage doch nochmals nach, dann sehen wir auch, ob Du es verstanden hast oder nicht. |
AW: Query an Gameserver
Schnipsel abgeändert:
Delphi-Quellcode:
1.) [Pascal Fehler] Unit4.pas(125): E2026 Konstantenausdruck erwartet
type
TBytes = Array of Byte; TValveByte = Byte; {8 bit character or unsigned integer} TValveShort = SmallInt; {16 bit signed integer} TValveLong = Integer; {32 bit signed integer} TValveFloat = Single; {32 bit floating point} TValveLongLong = Cardinal; {64 bit unsigned integer} const SIMPLE_RESPONSE_HEADER : TValveLong = $FFFFFFFF; MULTIPACKET_RESPONSE_HEADER : TValveLong = $FFFFFFFE; procedure Foo(AResponse : Array of Byte); var LResponseStream : TStream; LHeader : TValveLong; begin LResponseStream := TMemoryStream.Create(); LResponseStream.Write(AResponse, Sizeof(AResponse)); try LResponseStream.Read( LHeader, sizeof(LHeader) ); // Wir lesen den 4-Byte Header case LHeader of SIMPLE_RESPONSE_HEADER : ShowMessage('Simple Header'); <--- Fehler 1 // WorkOnSimpleResponse( LResponseStream ); <--- Fehler 2 MULTIPACKET_RESPONSE_HEADER : ShowMessage('Multi Header'); <--- Fehler 1 // WorkOnMultiPacketResponse( LResponseStream ); <--- Fehler 2 else raise Exception.CreateFmt( 'Unknown Header Value %8.8x', [LHeader] ); end; finally LResponseStream.Free; end; end; 2.) Nicht deklarierter Bezeichner 'WorkOnSimpleResponse' / 'WorkOnMultiPacketResponse' Zitat:
Zitat:
War das ein Angebot, wenn ich auf den Lerneffekt verzichte? Im Ernst jetzt mal... ich weis, dass ihr oft mit Kindern zu tun habt, die einfach nur schnell alles fertig haben wollen und sich nicht für den sogenannten Lerneffekt interessieren. Ich möchte mir verbieten, dass du mich mit jenen auf eine Stufe stellst, denn ich habe schon in einem der ersten Posts erwähnt, dass ich nichts fertiges möchte. Andererseits möchte ich auch kein 3 Jahres-Kurs belegen müssen um noch dieses Jahr an mein Ziel zu kommen. Ich erwarte von niemandem, dass er mir fertigen Code generiert, den ich nur noch CopyPasten muss um dann so zu tun als wenn ich der tollste bin weil es ja irgendwie läuft. Da habe ich doch nicht im Geringsten etwas von, wenn ich einmal etwas ändern muss. Ich kam hierher in der Hoffnung, dass mir auf einfache Art erklärt wird, was ich tun muss um ein paar Informationen von einem Gameserver zu bekommen. Und ich habe nicht ansatzweise damit gerechnet, auch nur diesen MONAT damit fertig zu werden. Das ich alleine mit dem WORT "Gameserver" in eine Schublade gesteckt werden würde, war mir schon VOR der Thread-Erstellung klar. Ich bitte dich nun, dich diesem Thema fernzuhalten, wenn du keine produktiven weiterführenden Informationen zur Verfügung stellen möchtest oder kannst. Ich werde dir nicht Mehr Offtopic antworten :thumb: MFG |
AW: Query an Gameserver
Zitat:
Zu den Fehlern:
|
AW: Query an Gameserver
Ich habe es jetzt gelöst bekommen, dass alles ausgelesen wird, auch wenn noch keine Map geladen ist oder keine Spieler vorhanden sind. Problem war ja dass dann "00" ausgegeben wird und sich das Array dann verschiebt. Sicherlich ist es unschön, aber es funktioniert. Für Verbesserungsvorschläge bin ich gerne offen.
Einziges Problem ist jetzt folgendes: Der Server startet 4 Mal am Tag neu. um 06:00, 12:00, 18:00 und 00:00 Uhr. Wenn man zu diesen Zeitpunkten den Query ausführt, hängt sich das Programm auf. Hier der Code:
Delphi-Quellcode:
Danke nochmal für die Hilfe bisher an alle die geholfen haben.
procedure TForm4.Button1Click(Sender: TObject);
var A: TStringList; udp: TUdpSocket; x: array[0..25] of byte; y: array[0..1400] of byte; i: integer; buff_delemitted: string; startbit: integer; endbitbit: integer; stringcounter: integer; begin memo1.Clear; udp := TUdpSocket.Create(Self); udp.RemoteHost:= edit1.Text; udp.RemotePort:= edit2.Text; udp.Active:= true; x[0]:= $FF; x[1]:= $FF; x[2]:= $FF; x[3]:= $FF; x[4]:= $54; x[5]:= $53; x[6]:= $6F; x[7]:= $75; x[8]:= $72; x[9]:= $63; x[10]:= $65; x[11]:= $20; x[12]:= $45; x[13]:= $6E; x[14]:= $67; x[15]:= $69; x[16]:= $6E; x[17]:= $65; x[18]:= $20; x[19]:= $51; x[20]:= $75; x[21]:= $65; x[22]:= $72; x[23]:= $79; x[24]:= $00; udp.SendBuf(x,sizeof(x)); udp.ReceiveBuf(y,sizeof(y)); udp.Active := false; for i := 4 to sizeof(y) - 1 do begin // Header Lesen if (i = 3) then begin if IntToHex(y[0], 2) + IntToHex(y[0], 1) + IntToHex(y[2], 2) + IntToHex(y[3], 2) = 'FFFFFFFF' then begin buff_delemitted := buff_delemitted + 'FFFFFFFF;'; end; if IntToHex(y[0], 2) + IntToHex(y[0], 1) + IntToHex(y[2], 2) + IntToHex(y[3], 2) = 'FFFFFFFE' then begin buff_delemitted := buff_delemitted + 'FFFFFFFE;'; end; end // Antwort else if (IntToHex(y[i], 2) = '49') then begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';' end // Protokoll else if i = 5 then begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';' end // Strings aufteilen nach 00 else if (IntToHex(y[i], 2) = '00') then begin stringcounter := stringcounter +1; // Erste 4 Strings aufteilen if stringcounter < 5 then buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';'; // Game ID Player und Max Player -> 0 Player keine Ausgabe "00" und würde Array verschieben if stringcounter = 5 then begin if IntToHex(y[i+2], 2) = '00' then buff_delemitted := buff_delemitted + IntToHex(y[i], 2) else buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';'; end; // Restliche Strings werden korrekt getrennt if stringcounter > 5 then buff_delemitted := buff_delemitted + IntToHex(y[i], 2) + ';'; end else begin buff_delemitted := buff_delemitted + IntToHex(y[i], 2) end; Application.ProcessMessages; end; // HEX in Memo anzeigen zur Kontrolle Memo1.Text := buff_delemitted; A := TStringList.Create; try Split(';', buff_delemitted, A) ; server_header := HexStrToString(a[0]); server_protocol := inttostr(hextoint(a[1])); server_protocol := IntToStr(HexToInt(a[1])); server_name := HexStrToString(a[2]); server_map := HexStrToString(a[3]); server_folder := HexStrToString(a[4]); server_game := HexStrToString(a[5]); server_ID := inttostr(hextoint(a[6][3] + a[6][4] + a[6][1] + a[6][2])); server_players := inttostr(hextoint(a[6][5] + a[6][6])); server_max_players := inttostr(hextoint(a[6][7] + a[6][8])); server_bots := inttostr(hextoint(a[6][9] + a[6][10])); server_type := HexStrToString(a[7][1] + a[7][2]); server_environment := HexStrToString(a[7][3] + a[7][4]); server_visibility := inttostr(hextoint(a[7][5] + a[7][6])); server_VAC := inttostr(hextoint(a[8])); server_version := inttostr(hextoint(a[11][3] + a[6][4] + a[6][1] + a[6][2])); server_ExtraDataFlag := ''; server_EDF_port := ''; server_EDF_steamID := ''; server_EDF_keywords := ''; server_EDF_gameID := ''; lblServerHeader.Caption := server_header; lblServerProtocol.Caption := server_protocol; lblServerName.Caption := server_name; lblServerMap.Caption := server_map; lblServerFolder.Caption := server_folder; lblServerGame.Caption := server_game; lblServerID.Caption := server_id; lblServerPlayers.Caption := server_players; lblServerMaxPlayers.Caption := server_max_players; lblServerBots.Caption := server_bots; lblServerType.Caption := server_type; lblServerEnvironment.Caption := server_environment; lblServerVisibility.Caption := server_visibility; lblServerVAC.Caption := server_VAC; lblServerVersion.Caption := server_version; finally A.Free; end; end; |
AW: Query an Gameserver
Prüfe nach
Delphi-Quellcode:
(besser wäre
udp.Active:= true;
Delphi-Quellcode:
), ob die Verbindung überhaupt hergestellt werden konnte.
udp.Open;
Delphi-Quellcode:
udp.Open;
if (udp.Connected) then begin x[0]:= $FF; x[1]:= $FF; //... udp.SendBuf(x,sizeof(x)); //... end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:54 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz