Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Query an Gameserver (https://www.delphipraxis.net/183987-query-gameserver.html)

Flodding 18. Feb 2015 10:17

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: https://github.com/xPaw/PHP-Source-Query-Class

Hier ein Link zum bereits konfigurierten Script:
http://last-templers.de/a2epoch/query/query.php

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: http://www.planet-source-code.com/vb...=2022&lngWId=7

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:
ÿÿÿÿI[L-T-S] Epoch Origins (1.0.5.1/125548)Taviarma2arrowpcDayZ Epoch Origins&#352;&#8222;dw1.63.125548±þ   `7'X@bt,r163,n125548,s7,i1,mf,lf,vf,dt,tcoop,g65545,c2147483647-2147483647,pw,&#352;&#8222;&#382;Pÿÿÿÿ&#8230;Nbº~PP3½Àü0óÿÿÿ¸ò#¸òf/D¸òÀü0ó&#382;ÂBÀü0ójñCz'ÀüPÄñ3½Àü&#8222;óÿÿÿó#óf/DóÀü&#8222;ó&#382;ÂBÀü&#8222;ójñCz'Àü,òجq¼!8x7P7wqbj¹u<ò&#8211;q¦'&#402;Hòb¹u0Àu&#8222;ò4k¹uk¹u²aº~¦'&#402; &#8221;&#381;/}¹u&#8364;òëUÑv³' &#8221;&#381;â`¹uht¹u³'; óðû)sP³' &#8221;&#381;ÿû)soåîYøò².DÀüz'Àüz'óùJA°Y<Ba<BÀüÜ;&DX©§0wh&#8364;îþîþ
&#338;èÀü@ó"¶A$ôZ<+w
&#339;±0w$ôÎW-wÓ<+w§È¹w6&#338;P&#338;z'|óP&#338;Ðt&#338;äóm¹uP&#338;(óº
pÿÿÿÿPØû&#732;&#376;îYäõ&#732;&#376;&#338;&#352;¦0wh&#8364;îþîþ&#338;P`(Øûõ&#338;è¬0wЪ8+w&#8220;ɹw&#338;&#338;P&#732;&#376;P3½³'P&#338;&#338;P&#338;(jñC¬ô5w&#338;(à&#352;5w&#732;&#376;&#8364;&#338;&#338;P&#338;&#8222;ö(õqeW0ôÐW3whõ4õ5w&#338;&#338;ê¸*w&#338;&#338;ê¸*wxõ°5w8&#338;&#8221;5wûɹw&#338;
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.

Weiterführende Informationen sollte ich hier bekommen:

Quelle: https://developer.valvesoftware.com/wiki/Server_queries

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:
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;
Beim Kompilieren kommt bei mir die Meldung:
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

Sir Rufo 18. Feb 2015 10:46

AW: Query an Gameserver
 
Was hast du denn für eine Ausgabe erwartet?
Zitat:

Hallo, ich bins, der Tuberkel!
Du bekommst dort eine Bytefolge, die du natürlich entsprechend entschlüsseln musst. An irgendeiner Stelle werden diese Informationen stehen. Dazu muss man aber den Kontext (Struktur/Aufbau) kennen. Ohne diesen Kontext wirst du das niemals (nur sehr schwer) herausfinden.

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.

BadenPower 18. Feb 2015 11:11

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290324)
Beim Kompilieren kommt bei mir die Meldung:
Die Methode udp1Receive, auf die udp1.OnReceive verweist, enthält eine inkompatible Parameterliste. Soll der Verweis entfernt werden?

Was ist "udp1" für eine Komponente?
Und poste noch die Deklaration der Methode udp1Receive welche dem OnReceive zugewiesen wurde.

Flodding 18. Feb 2015 11:45

AW: Query an Gameserver
 
Zitat:

Du bekommst dort eine Bytefolge, die du natürlich entsprechend entschlüsseln musst. An irgendeiner Stelle werden diese Informationen stehen. Dazu muss man aber den Kontext (Struktur/Aufbau) kennen. Ohne diesen Kontext wirst du das niemals (nur sehr schwer) herausfinden.
Das ist das erste Mal, dass ich mit damit arbeite.

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:

Zitat von BadenPower (Beitrag 1290334)
Was ist "udp1" für eine Komponente?
Und poste noch die Deklaration der Methode udp1Receive welche dem OnReceive zugewiesen wurde.

udp1 ist ein TUdpSocket

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;

BadenPower 18. Feb 2015 12:13

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290340)
Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PWideChar; var DataLen: Integer);

Stell das mal um und Du hast eine Fehlermeldung weniger.

Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);

Und dann schau Dir die erhaltenen Daten noch mals an.

Neutral General 18. Feb 2015 12:25

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290343)
Zitat:

Zitat von Flodding (Beitrag 1290340)
Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PWideChar; var DataLen: Integer);

Stell das mal um und Du hast eine Fehlermeldung weniger.

Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);

Und dann schau Dir die erhaltenen Daten noch mals an.

Dann wirds mit Sicherheit nicht mal compilieren. Ja probier das mal :mrgreen:

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...

BadenPower 18. Feb 2015 12:29

AW: Query an Gameserver
 
Zitat:

Zitat von Neutral General (Beitrag 1290348)
Dann wirds mit Sicherheit nicht mal compilieren.

Wie kommst Du auf diese schmale Brett???

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.

Neutral General 18. Feb 2015 12:32

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290350)
Zitat:

Zitat von Neutral General (Beitrag 1290348)
Dann wirds mit Sicherheit nicht mal compilieren.

Wie kommst Du auf diese schmale Brett???

Oh tut mir Leid. Hatte zu wenig gelesen und zu viel angenommen.
Du hast Recht.

Flodding 18. Feb 2015 12:36

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290343)
Zitat:

Zitat von Flodding (Beitrag 1290340)
Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PWideChar; var DataLen: Integer);

Stell das mal um und Du hast eine Fehlermeldung weniger.

Delphi-Quellcode:
procedure udp1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);

Und dann schau Dir die erhaltenen Daten noch mals an.

Das Ergebnis unterscheidet sich nicht ...

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 ?

Sir Rufo 18. Feb 2015 12:46

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:
42d => 2Ah
A -> Ah => 10d / 15d = 66.66%
B -> 2h => 2d / 15d = 13.33%
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.

Flodding 18. Feb 2015 13:05

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...

BadenPower 18. Feb 2015 13:09

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290324)
Aktuell regel ich das über ein PHP-Query-Script, da ich dies als fertige Lösung gefunden habe.

Quelle: https://github.com/xPaw/PHP-Source-Query-Class

Der Link funktioniert nicht.

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:

Zitat von Flodding (Beitrag 1290324)
Hallo guten Morgen :cry:
Hier ein Link zum bereits konfigurierten Script:
http://last-templers.de/a2epoch/query/query.php

Ist das der lauffähige Link zum Script oben?

Neutral General 18. Feb 2015 13:12

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290359)
Okay also aus deinem Post verstehe ich denn jetzt, dass ich das nicht hinbekommen kann, weil ich den Kontext nicht habe oder verstehe?!

Doch du kannst es hinbekommen weil Valve ja gerade so nett war und dokumentiert hat welche Bytes wie/als was zu interpretieren sind:

https://developer.valvesoftware.com/wiki/Server_queries

Sir Rufo 18. Feb 2015 13:13

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:

Zitat von Neutral General (Beitrag 1290362)
Zitat:

Zitat von Flodding (Beitrag 1290359)
Okay also aus deinem Post verstehe ich denn jetzt, dass ich das nicht hinbekommen kann, weil ich den Kontext nicht habe oder verstehe?!

Doch du kannst es hinbekommen weil Valve ja gerade so nett war und dokumentiert hat welche Bytes wie/als was zu interpretieren sind:

https://developer.valvesoftware.com/wiki/Server_queries

Aha, da ist ja das fehlende Puzzleteil ;)

Flodding 18. Feb 2015 17:09

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290361)
Zitat:

Zitat von Flodding (Beitrag 1290324)
Aktuell regel ich das über ein PHP-Query-Script, da ich dies als fertige Lösung gefunden habe.

Quelle: https://github.com/xPaw/PHP-Source-Query-Class

Der Link funktioniert nicht.

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:

Zitat von Flodding (Beitrag 1290324)
Hallo guten Morgen :cry:
Hier ein Link zum bereits konfigurierten Script:
http://last-templers.de/a2epoch/query/query.php

Ist das der lauffähige Link zum Script oben?



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:
<?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 );
      }
   }
Offensichtlich werden hier die Bytes für das PHP-Script definiert und ausgewertet, entschlüsselt und oder decodiert ...

BadenPower 18. Feb 2015 17:25

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290438)
Offensichtlich werden hier die Bytes für das PHP-Script definiert und ausgewertet, entschlüsselt und oder decodiert ...

Da hast Du doch alles, was Du benötigst.

Jetzt musst Du halt nur noch den PHP-Code in Delphi nachbilden.

Also einfacher kann man es nun wirklich nicht haben.

Flodding 18. Feb 2015 17:31

AW: Query an Gameserver
 
Code:
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;
            }
Hier werden die Spieler auf dem Server erkannt ja?

Im oberen Teil des Codes finde ich :

Code:
const A2S_PLAYER   = 0x55;
Dies sollte bedeuten, dass der Buffer den ich senden muss so aussehen sollte:

FF FF FF FF 55

Dabei fehlt mir dann aber dieses Challenge ... das versteh ich nicht was damit gemeint ist. :oops:

BadenPower 18. Feb 2015 17:39

AW: Query an Gameserver
 
Hast Du Dir diese Seite schon angesehen, welche Neutral General bereits gepostet hat?

https://developer.valvesoftware.com/wiki/Server_queries

Flodding 18. Feb 2015 17:52

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.

Flodding 18. Feb 2015 18:39

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:
ÿÿÿÿ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¥…ûƒ òd q¶c…ûƒûƒ€1/}¹u/}¹u4òجqJ4p;H;wqbj¹u(ò– qûƒ4òb¹u0Àuôpò4k¹uk¹uÎvûƒ€1/}¹ulòëUÑvY Z€1â`¹uht¹uY Z; óðû)s¶cY Z€1ÿû)sò°´FY Z€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
ÿÿÿÿA heisst ja nun dass es die antwort auf die Anfrage ist ein Challenge zu bekommen...

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?!:?

Flodding 18. Feb 2015 19:52

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:
$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;
Ich weiss leider nicht, wie ich in Delphi aus dem Byte was zurück kommt nur die Strings auslese...

Sir Rufo 18. Feb 2015 20:43

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
  1. Header:
    Delphi-Quellcode:
    byte
  2. Payload:
    Delphi-Quellcode:
    string
Code:
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
Schauen wir uns das Datenpaket an, dann finden wir die ersten 4 Bytes mit
Delphi-Quellcode:
FF
, dann den Header mit
Delphi-Quellcode:
54
und der Rest ist der String ... öh, bis auf das letzte Byte
Delphi-Quellcode:
00
.

Nächstes Beispiel angesehen
Code:
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.
Aha, da sind ja wieder diese
Delphi-Quellcode:
00
Bytes zwischen den einzelnen Strings. Sehr schön.

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).

Flodding 18. Feb 2015 20:54

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:
udp1.ReceiveBuf(y,sizeof(y));

players := IntToStr(y[5]);
label7.Caption := players;
Das geschieht gleich nachdem ich das Paket mit dem Challengekey gesendet habe.

p80286 18. Feb 2015 23:19

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:

Zitat von Flodding (Beitrag 1290491)
Habs jetzt per Zufall ..

Voodoo, Lotto, Zauberei??

Flodding 19. Feb 2015 16:29

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:
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
Da haben wir die besagten doppel 00en von denen vorher gesprochen wurde.
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 ?

Medium 19. Feb 2015 16:50

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.

Flodding 22. Feb 2015 10:32

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.
https://github.com/xPaw/PHP-Source-Q...cket.class.php

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:
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;
jetzt das ganze mit Spieler und Map
Code:
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;
Die Unterschiede konnte ich leider nicht FETT markieren, habe sie aber kommentiert.

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:
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;
MFG

Sir Rufo 22. Feb 2015 11:45

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
NameDescriptionByte-Länge
byte8 bit character or unsigned integer1 Byte
short16 bit signed integer2 Byte
long32 bit signed integer4 Byte
float32 bit floating point4 Byte
long long64 bit unsigned integer8 Byte
stringvariable-length byte field, encoded in UTF-8, terminated by 0x00solange ein Byte lesen, bis man $00 liest

Wenn ich dann lese, dass du die ID (Steam Application ID of game) da vermutest
Code:
8A8400; Diese beiden Zeilen sollten eigentlich
3300;  Eine Zeile sein. 00 Problem. Sie beinhalten die ID (33930), Anzahl Spieler und Max Spieler
dann hast du die Dokumentation nämlich nicht gelesen.

Wenn ich jetzt mal deine Daten dem Response Format laut Dokumentation zuordne, dann erhalte ich
FieldTypeBytesRAW-Data
Headerbyte149
Protocolbyte111
Namestringvar5B4C2D542D535D2045706F6368204F726967696E732028312E302E352E312F3132353534382900
Mapstringvar5461766900
Folderstringvar61726D61326172726F77706300
Gamestringvar4461795A2045706F6368204F726967696E7300
IDshort28A84
Playersbyte100
Max. Playersbyte133
Botsbyte100
......... 
Vergleiche das mit deinen Angaben und du solltest den Fehler feststellen.

Flodding 22. Feb 2015 12:02

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.

BadenPower 22. Feb 2015 12:16

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1290958)
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.

Das sagt mir, dass Du nie versucht hast 33930 in ein Hex umzuwandeln, denn dann hättest Du gesehen , dass 33930 ToHex-> 848A ist.

Na, fällt Dir etwas auf?

Flodding 22. Feb 2015 12:18

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.

Sir Rufo 22. Feb 2015 13:17

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
http://docwiki.embarcadero.com/RADSt...Der_Typ_Single

Dann könnte man das hier bauen
Delphi-Quellcode:
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}
Jetzt gibt es in Delphi auch so nette Sachen wie Delphi-Referenz durchsuchenTStream und der kann Delphi-Referenz durchsuchenTStream.WriteData und (viel Spanier hier) Delphi-Referenz durchsuchenTStream.ReadData.

Du bekommst ja eine Handvoll Bytes und die kann man in einen Delphi-Referenz durchsuchenTBytesStream packen und dann liest man ganz gemütlich die Daten heraus.

Hier mal ein kleines Beispiel zum Starten:
Delphi-Quellcode:
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;
Wie du sehen kannst, vermeide ich auf Teufel komm raus, irgendwo im Quelltext mit Magic Values zu arbeiten.

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.

Sir Rufo 22. Feb 2015 13:35

AW: Query an Gameserver
 
Bist du dir auch sicher, dass
Delphi-Quellcode:
[$8A, $84]
auch wirklich
Delphi-Quellcode:
$8A84
bedeuten oder evtl. eher
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
http://de.wikipedia.org/wiki/Byte-Re...-Endian-Format

und dann bedeutet diese Bytefolge
Delphi-Quellcode:
[$8A, $84]
eben genau
Delphi-Quellcode:
$848A
entspricht was dezimal dann tatsächlich
Delphi-Quellcode:
33930
entspricht. Nur im Speicher sind die Bytes eben in umgekehrter Reihenfolge abgelegt.

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:
SmallInt
, bzw.
Delphi-Quellcode:
Int16
)

BadenPower 22. Feb 2015 14:27

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290960)
Zitat:

Zitat von Flodding (Beitrag 1290958)
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.

Das sagt mir, dass Du nie versucht hast 33930 in ein Hex umzuwandeln, denn dann hättest Du gesehen , dass 33930 ToHex-> 848A ist.

Na, fällt Dir etwas auf?


Zitat:

Zitat von Sir Rufo (Beitrag 1290969)
und dann bedeutet diese Bytefolge
Delphi-Quellcode:
[$8A, $84]
eben genau
Delphi-Quellcode:
$848A
entspricht was dezimal dann tatsächlich
Delphi-Quellcode:
33930
entspricht. Nur im Speicher sind die Bytes eben in umgekehrter Reihenfolge abgelegt.

Vielleicht fällt es ihm jetzt auf, wenn er das 2. mal mit der Nase darauf gestoßen wird.

Flodding 22. Feb 2015 18:39

AW: Query an Gameserver
 
Zitat:

Zitat von BadenPower (Beitrag 1290975)
Vielleicht fällt es ihm jetzt auf, wenn er das 2. mal mit der Nase darauf gestoßen wird.

Ja ist es jetzt.
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:
procedure Foo( AResponse : TBytes );
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.

BadenPower 22. Feb 2015 19:19

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1291007)
Man schaue sich "8A84" und "848A" nur mal an.

Ich ICH ich

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:

Zitat von Flodding (Beitrag 1291007)
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.

Das war nicht meine Absicht, zeigt mir aber, wie so oft, dass Antworten leider von den Fragestellern nicht richtig angeschaut werden.

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.

Flodding 22. Feb 2015 19:47

AW: Query an Gameserver
 
Schnipsel abgeändert:
Delphi-Quellcode:
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;
1.) [Pascal Fehler] Unit4.pas(125): E2026 Konstantenausdruck erwartet
2.) Nicht deklarierter Bezeichner 'WorkOnSimpleResponse' / 'WorkOnMultiPacketResponse'

Zitat:

Man sieht sofort den Unterschied, da es in der Muttersprache geschrieben steht.
Entschuldige, dass ich Delphi nicht wie meine Muttersprache beherrsche.

Zitat:

Wir können Dir auch das Programm komplett schreiben, aber wo wäre da der Lerneffekt.
@ BadenPower

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

BUG 22. Feb 2015 23:55

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1291023)
Entschuldige, dass ich Delphi nicht wie meine Muttersprache beherrsche.

Das ist auch nicht schlimm ... Sprachen und Bibliotheken kommen und gehen. Aber als Entwickler/Programmierer ist man zu einem großem Teil Problemlöser. Deine Fragen deuten darauf hin, dass in deinem Entwickler-Werkzeugkasten noch ein paar wichtige Werkzeuge fehlen. Daher also der ganze "Belehrungskram" :wink:

Zu den Fehlern:
  1. Ich würde mutmaßen, dass deine Delphiversion nicht mit den typisierten Konstanten im Case zurechtkommt. Probiere es mal ohne die Typen oder benutze stattdessen if-then-else-if...
  2. Diese beiden Funktionen bekommen den "Rest" des Streams und sollten von dir selbst geschrieben werden.
Der Code von Sir Rufo ist eben nur ein Beispiel wie man so etwas angehen kann.

Flodding 24. Feb 2015 11:34

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:
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;
Danke nochmal für die Hilfe bisher an alle die geholfen haben.

BadenPower 24. Feb 2015 12:36

AW: Query an Gameserver
 
Prüfe nach
Delphi-Quellcode:
udp.Active:= true;
(besser wäre
Delphi-Quellcode:
udp.Open;
), ob die Verbindung überhaupt hergestellt werden konnte.

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 01:23 Uhr.
Seite 1 von 3  1 23      

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