Delphi-PRAXiS
Seite 3 von 3     123   

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 25. Feb 2015 10:57

AW: Query an Gameserver
 
Nachdem das OnError Event deaktiviert wurde, funktionierte dann auch "bmNonBlocking"

Der Workaround funktioniert zwar, aber die Abfragezeit ist mit 5 Sekunden etwas lang.
Delphi-Quellcode:
  form4.udp1.BlockMode := bmNonBlocking;
  form4.udp1.RemoteHost:= form4.edit1.Text;
  form4.udp1.RemotePort:= form4.edit2.Text;
  form4.udp1.Open;

  if form4.udp1.Connected then
  begin
  form4.Udp1.SendBuf(request, SizeOf(TInfoRequest));
  form4.udp1.WaitForData(250);
  form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
  i := 0;
Dies funktioniert so ohne extra Zeitüberprüfung. Ob Queryport offen oder eben nicht.

BadenPower 25. Feb 2015 11:04

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1291432)
Der Workaround funktioniert zwar, aber die Abfragezeit ist mit 5 Sekunden etwas lang.

Diese Zeit ist nur 5 Sekunden, wenn der Server oder Port nicht erreichbar ist. Ist er Erreichbar, dann rutscht es in Milisekunden durch.

Zitat:

Zitat von Flodding (Beitrag 1291432)
Nachdem das OnError Event deaktiviert wurde, funktionierte dann auch "bmNonBlocking"

wenn ich den UdpSocket auf "bmNonBlocking" stelle, dann blockiert er nie. Aber ich bekomme in diesem Modus keine Daten.

Flodding 25. Feb 2015 11:09

AW: Query an Gameserver
 
Dies hier funktioniert bei mir:
Delphi-Quellcode:
procedure Get_A2S_INFO;
var
  request: TInfoRequest;
  response: TInfoResponse;
  buffer: TBufferArray;
  i: Integer;
  CheckTime: TDateTime;
begin
  request.RequestHeader := -1;
  request.PacketHeader := 'T';
  request.Payload := 'Source Engine Query'; // Von mir aus auch mit einer Konstante oder einem Ressourcestring
  request.Payload[19] := #0;

  form4.udp1.BlockMode := bmNonBlocking;
  form4.udp1.RemoteHost:= form4.edit1.Text;
  form4.udp1.RemotePort:= form4.edit2.Text;
  form4.udp1.Open;

  if form4.udp1.Connected then
    begin
    form4.Udp1.SendBuf(request, SizeOf(TInfoRequest));
    form4.udp1.WaitForData(250);
    form4.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
    i := 0;
    Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
    Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader));
    Move(buffer[i], response.Protocol, SizeOf(response.Protocol)); inc(i, SizeOf(response.Protocol));

    if response.ResponseHeader = -1 then
      begin
      response.Name := GetString(buffer, i);
      response.Map := GetString(buffer, i);
      response.Folder := GetString(buffer, i);
      response.Game := GetString(buffer, i);
      Move(buffer[i], response.ID, SizeOf(response.ID)); inc(i, SizeOf(response.ID));
      Move(buffer[i], response.Players, SizeOf(response.Players)); inc(i, SizeOf(response.Players));
      Move(buffer[i], response.MaxPlayers, SizeOf(response.MaxPlayers)); inc(i, SizeOf(response.MaxPlayers));
      Move(buffer[i], response.Bots, SizeOf(response.Bots)); inc(i, SizeOf(response.Bots));
      Move(buffer[i], response.ServerType, SizeOf(response.ServerType)); inc(i, SizeOf(response.ServerType));
      Move(buffer[i], response.Environment, SizeOf(response.Environment)); inc(i, SizeOf(response.Environment));
      Move(buffer[i], response.Visibility, SizeOf(response.Visibility)); inc(i, SizeOf(response.Visibility));
      Move(buffer[i], response.VAC, SizeOf(response.VAC)); inc(i, SizeOf(response.VAC));
      response.Version := GetString(buffer, i);
      Move(buffer[i], response.EDF, SizeOf(response.EDF)); inc(i, SizeOf(response.EDF));
      Move(buffer[i], response.EDFPort, SizeOf(response.EDFPort)); inc(i, SizeOf(response.EDFPort));
      Move(buffer[i], response.EDFSteamID, SizeOf(response.EDFSteamID)); inc(i, SizeOf(response.EDFSteamID));
      Move(buffer[i], response.EDFSpecPort, SizeOf(response.EDFSpecPort)); inc(i, SizeOf(response.EDFSpecPort));
      //response.EDFSpecName := GetString(buffer, i);
      response.EDFKeywords := GetString(buffer, i);
      Move(buffer[i], response.EDFGameID, SizeOf(response.EDFGameID)); inc(i, SizeOf(response.EDFGameID));

      form4.lblServerHeader.Caption := chr(response.PacketHeader);
      form4.lblServerProtocol.Caption := IntToStr(response.Protocol);
      form4.lblServerName.Caption := response.Name;
      form4.lblServerMap.Caption := response.Map;
      form4.lblServerFolder.Caption := response.Folder;
      form4.lblServerGame.Caption := response.Game;
      form4.lblServerID.Caption := inttostr(response.ID);
      form4.lblServerPlayers.Caption := IntToStr(response.Players);
      form4.lblServerMaxPlayers.Caption := IntToStr(response.MaxPlayers);
      form4.lblServerBots.Caption := IntToStr(response.Bots);
      form4.lblServerType.Caption := chr(response.ServerType);
      form4.lblServerEnvironment.Caption := chr(response.Environment);
      form4.lblServerVisibility.Caption := inttostr(response.Visibility);
      form4.lblServerVAC.Caption := IntToStr(response.VAC);
      form4.lblServerVersion.Caption := response.Version;
      end
      else
      form4.lblServerName.Caption := 'Server nicht erreichbar';
  end;
end;
Das Error Event:
Delphi-Quellcode:
procedure TForm4.udp1Error(Sender: TObject; SocketError: Integer);
begin
//showmessage('Could not connect. The server you are trying to query might be down');
//udp1.Active:= false;
end;

Flodding 25. Feb 2015 14:32

AW: Query an Gameserver
 
Die Sekundenanzeige bei Der Playerinfo will auch nicht so ganz.

Delphi-Quellcode:
TPlayerResponse = record
  ResponseHeader: Integer;
  PacketHeader: Byte;
  Challenge: Integer;
  PlayerCount: Byte;
  PlayerIndex: Byte;
  PlayerName: String;
  PlayerScore: Integer;
  PlayerDuration: Integer;
end;
Delphi-Quellcode:
procedure Get_PLAYER_INFO;
var
  request: TInfoRequest;
  response: TPlayerResponse;
  buffer: TBufferArray;
  i: Integer;
  y: integer;
  PlayerList: TTreeNode;
begin
  request.RequestHeader := -1;
  request.PacketHeader := 'U';
  request.Payload := 'FFFFFFFF';

  Form1.udp1.BlockMode := bmNonBlocking;
  Form1.udp1.RemoteHost:= Form1.edit1.Text;
  Form1.udp1.RemotePort:= Form1.edit2.Text;
  Form1.udp1.Open;

  if (Form1.udp1.Connected) then
    begin
    Form1.Udp1.SendBuf(request, SizeOf(TInfoRequest));
    Form1.udp1.WaitForData(250);
    Form1.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
    //Challenge aus Buffer lesen
    i := 0;
    Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
    Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader));
    Move(buffer[i], response.Challenge, SizeOf(response.Challenge)); inc(i, SizeOf(response.Challenge));
    if chr(response.PacketHeader) = 'A' then
      begin
      //Challende in den PayLoad schreiben
      request.Payload[0] := chr(buffer[5]);
      request.Payload[1] := chr(buffer[6]);
      request.Payload[2] := chr(buffer[7]);
      request.Payload[3] := chr(buffer[8]);
      //A2S_PLAYER request mit Challenge senden
      Form1.Udp1.SendBuf(request, SizeOf(TInfoRequest));
      Form1.udp1.WaitForData(250);
      Form1.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
      i := 0;
      Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
      Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader));
      if chr(response.PacketHeader) = 'D' then
        begin
        Move(buffer[i], response.PlayerCount, SizeOf(response.PlayerCount)); inc(i, SizeOf(response.PlayerCount));
        Form1.TreeView1.Items.Clear;
        for y := 0 to response.PlayerCount - 1 do
          begin
          Move(buffer[i], response.PlayerIndex, SizeOf(response.PlayerIndex)); inc(i, SizeOf(response.PlayerIndex));
          response.PlayerName := GetString(buffer, i);
          Move(buffer[i], response.PlayerScore, SizeOf(response.PlayerScore)); inc(i, SizeOf(response.PlayerScore));
          Move(buffer[i], response.PlayerDuration, SizeOf(response.PlayerDuration)); inc(i, SizeOf(response.PlayerDuration));

          PlayerList := Form1.TreeView1.Items.Add(Nil, response.PlayerName);
          Form1.TreeView1.Items.AddChild(PlayerList, 'ID: ' + IntToStr(response.PlayerIndex));
          Form1.TreeView1.Items.AddChild(PlayerList, 'Kills: ' + IntToStr(response.PlayerScore));
          Form1.TreeView1.Items.AddChild(PlayerList, 'Seconds: ' + IntToStr(response.PlayerDuration));

          Application.ProcessMessages;
          end;
        end;
      end;
    end;
form1.TreeView1.FullExpand;
Form1.udp1.Active := false;
end;
Lese ich die Zeit flasch aus?

Neutral General 25. Feb 2015 14:39

AW: Query an Gameserver
 
Wie immer gilt: Dokumentation lesen hilft.

Data Type Comment
Duration float Time (in seconds) player has been connected to the server.

Flodding 25. Feb 2015 16:19

AW: Query an Gameserver
 
LongInt wäre da richtig oder?

http://www.delphibasics.co.uk/RTL.asp?Name=LongInt

Neutral General 25. Feb 2015 16:28

AW: Query an Gameserver
 
Float ist eine Fließkommazahl. Single wäre der entsprechende Datentyp in Delphi.

Flodding 25. Feb 2015 20:25

AW: Query an Gameserver
 
Ich versuche jetzt die Master-Serverliste abzufragen.

Ich bekomme sogar Antwort. :thumb:

Ich hab jetzt etliche Tests gemacht und rumprobiert, aber ich bekomme einfach keine vernünftige Liste mit servern die ich dann per Query abfragen könnte.

Delphi-Quellcode:
TServerListResponse = record
  ResponseHeader: Integer;
  PacketHeader: Word;
  First: Byte;               // Byte
  Secound: Byte;             // Byte
  Third: Byte;               // Byte
  Fourth: Byte;              // Byte
  Port: Word;                // Short
end;
Delphi-Quellcode:
procedure Get_SERVER_LIST;
var
  request: TInfoRequest;
  response: TServerListResponse;
  buffer: TBufferArray;
  i: Integer;
  y: integer;
begin
// Abfrage der Arma 2 OA Server

// Example
// 31 FF 30 2E 30 2E 30 2E 30 3A 30 00 5C 6E 61 70 70 5C 35 30 30 00

// Tests
//       1ÿ0.0.0.0:0\x00\gamedir\arma2arrowpc\x00
//       31 ff 30 2e 30 2e 30 2e 30 3a 30 5c 78 30 30 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 5c 78 30 30
//      1ÿ0.0.0.0:0\x00\gamedir\arma2arrowpc\x00
//      31 ff 30 2e 30 2e 30 2e 30 3a 30 5c 30 30 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 5c 30 30
//       1ÿ0.0.0.0:0\00\gamedir\arma2arrowpc\00
//       31 ff 30 2e 30 2e 30 2e 30 3a 30 5c 30 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 5c 30
//      1ÿ0.0.0.0:000\gamedir\arma2arrowpc00
//      31 ff 30 2e 30 2e 30 2e 30 3a 30 30 30 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 30 30
//       1ÿ0.0.0.0:0ß\gamedir\arma2arrowpcß
//       31 ff 30 2e 30 2e 30 2e 30 3a 30 df 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 df
//      1ÿ0.0.0.0:00\gamedir\arma2arrowpc0
//      31 ff 30 2e 30 2e 30 2e 30 3a 30 30 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 30

  request.Payload[0] := $31;
  request.Payload[1] := $FF;
  request.Payload[2] := $30;
  request.Payload[3] := $2E;
  request.Payload[4] := $30;
  request.Payload[5] := $2E;
  request.Payload[6] := $30;
  request.Payload[7] := $2E;
  request.Payload[8] := $30;
  request.Payload[9] := $3A;
  request.Payload[10] := $00;

  request.Payload[11] := $30;
  request.Payload[12] := $5C;
  request.Payload[13] := $67;
  request.Payload[14] := $61;
  request.Payload[15] := $6D;
  request.Payload[16] := $65;
  request.Payload[17] := $64;
  request.Payload[18] := $69;
  request.Payload[19] := $72;
  request.Payload[20] := $5C;
  request.Payload[21] := $61;
  request.Payload[22] := $72;
  request.Payload[23] := $6D;
  request.Payload[24] := $61;
  request.Payload[25] := $32;
  request.Payload[26] := $61;
  request.Payload[27] := $72;
  request.Payload[28] := $72;
  request.Payload[29] := $6F;
  request.Payload[30] := $77;
  request.Payload[31] := $70;
  request.Payload[32] := $63;
  request.Payload[33] := $00;

  Form1.udp1.BlockMode := bmNonBlocking;
  Form1.udp1.RemoteHost:= 'hl2master.steampowered.com';
  Form1.udp1.RemotePort:= '27011';
  Form1.udp1.Open;

  if (Form1.udp1.Connected) then
    begin
    Form1.Udp1.SendBuf(request.Payload, SizeOf(request.Payload));
    Form1.udp1.WaitForData(250);
    Form1.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
    i := 0;
    Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
    form1.ListBox1.Clear;
    for y := 0 to 255 do
      begin
      Move(buffer[i], response.First, SizeOf(response.First)); inc(i, SizeOf(response.First));
      Move(buffer[i], response.Secound, SizeOf(response.Secound)); inc(i, SizeOf(response.Secound));
      Move(buffer[i], response.Third, SizeOf(response.Third)); inc(i, SizeOf(response.Third));
      Move(buffer[i], response.Fourth, SizeOf(response.Fourth)); inc(i, SizeOf(response.Fourth));
      Move(buffer[i], response.Port, SizeOf(response.Port)); inc(i, SizeOf(response.Port));
      Form1.ListBox1.Items.Add(Inttostr(response.First) + '.' + Inttostr(response.Secound) + '.' + inttostr(response.Third) + '.' + inttostr(response.Fourth) + ':' + FloatToStr(response.Port));
      Application.ProcessMessages;
      end;

    end;

Form1.udp1.Active := false;
end;
Danke im Voraus diesmal :oops:

Sir Rufo 25. Feb 2015 20:42

AW: Query an Gameserver
 
Was wollen einem wohl diese Worte sagen:
Zitat:

The format is then a series of these server address blocks
und dann noch (weil die Frage bestimmt auch noch kommt)
Zitat:

The full requested IP list may not fit in 1 x packet. Subsequent packets must be requested by using the last received IP address and Port as the 'seed' in the next request.
Das erste Zitat veranlasst uns zu folgendem Konstrukt
Delphi-Quellcode:
TServerAddressBlock = record
  First: Byte; // Byte
  Second: Byte; // Byte
  Third: Byte; // Byte
  Fourth: Byte; // Byte
  Port: Word; // unsigned Short
end;

TServerAddresses = array of TServerAddressBlock;

TServerListResponse = record
  ResponseHeader: Integer;
  PacketHeader: Word;
  ServerAddresses : TServerAddresses;
end;

BadenPower 25. Feb 2015 20:54

AW: Query an Gameserver
 
Delphi-Quellcode:
  lRequest.Payload[9] := $3A;
  lRequest.Payload[10] := $00;
nach dem Doppelpunkt fehlt noch die 0 für den Port.

Flodding 25. Feb 2015 21:02

AW: Query an Gameserver
 
Ich hatte die Definitionen ja exakt ganauso schon gepostet.

Dass es mehrere Pakete werden für die komplette Serverliste hab ich auch schon verstanden.

Ich wollte erstmal nur ein Paket "sortieren" und glaube, dass ich einfach einen falschen Query ausführe.

Als Response bekomme ich IP Adressen von Servern die nicht dem Filter entsprechen.

Das kann doch nicht an der Aufteilung der Arrays liegen :?

Damit kommen auch nur falsche Server raus.
Delphi-Quellcode:
  request.Payload[9] := $3A;
  request.Payload[10] := $00;
  request.Payload[11] := $00;

  request.Payload[12] := $30;
  request.Payload[13] := $5C;
Damit auch:
Delphi-Quellcode:
  request.Payload[9] := $3A;
  request.Payload[10] := $00;
  request.Payload[11] := $30;

  request.Payload[12] := $30;
  request.Payload[13] := $5C;

BadenPower 25. Feb 2015 21:25

AW: Query an Gameserver
 
Dein altes aus Post #88

Delphi-Quellcode:
  request.Payload[0] := $31;
  request.Payload[1] := $FF;
  request.Payload[2] := $30;
  request.Payload[3] := $2E;
  request.Payload[4] := $30;
  request.Payload[5] := $2E;
  request.Payload[6] := $30;
  request.Payload[7] := $2E;
  request.Payload[8] := $30;
  request.Payload[9] := $3A; // hier ist der Doppelpunkt
  request.Payload[10] := $00; // StringEnde
fehlende 0 also die Zahl 0

Delphi-Quellcode:
  lRequest.Payload[0] := $31;
  lRequest.Payload[1] := $FF;
  lRequest.Payload[2] := $30;
  lRequest.Payload[3] := $2E;
  lRequest.Payload[4] := $30;
  lRequest.Payload[5] := $2E;
  lRequest.Payload[6] := $30;
  lRequest.Payload[7] := $2E;
  lRequest.Payload[8] := $30;
  lRequest.Payload[9] := $3A; // hier ist der Doppelpunkt
  lRequest.Payload[10] := $30; //Port 0 hat hier gefehlt
  lRequest.Payload[11] := $00; // StringEnde
Bitte, bitte, bitte Flo. Immer alles aufmerksam lesen.
Du machst es uns wirklich nicht leicht.

Flodding 25. Feb 2015 21:52

AW: Query an Gameserver
 
Ja entschuldigung .
Es sind meisst nur Flüchtigkeitsfehler und ich mag schon garnicht mehr weiter Fragen stellen weil ich mich langsam anfange über mich selber zu ärgern und schämen. :roll:

Dieser Query sollte doch eigentlich dann richtig sein:
Delphi-Quellcode:
  request.Payload[0] := $31; // die "1"
  request.Payload[1] := $FF; // ÿ für Weltweit
  request.Payload[2] := $30; // 0
  request.Payload[3] := $2E; // .
  request.Payload[4] := $30; // 0
  request.Payload[5] := $2E; // .
  request.Payload[6] := $30; // 0
  request.Payload[7] := $2E; // .
  request.Payload[8] := $30; // 0
  request.Payload[9] := $3A; // :
  request.Payload[10] := $30; // 0
  request.Payload[11] := $00; // erstes Stringende
  request.Payload[12] := $5C; // \
  request.Payload[13] := $67; // g
  request.Payload[14] := $61; // a
  request.Payload[15] := $6D; // m
  request.Payload[16] := $65; // e
  request.Payload[17] := $64; // d
  request.Payload[18] := $69; // i
  request.Payload[19] := $72; // r
  request.Payload[20] := $5C; // \
  request.Payload[21] := $61; // a
  request.Payload[22] := $72; // r
  request.Payload[23] := $6D; // m
  request.Payload[24] := $61; // a
  request.Payload[25] := $32; // 2
  request.Payload[26] := $61; // a
  request.Payload[27] := $72; // r
  request.Payload[28] := $72; // r
  request.Payload[29] := $6F; // o
  request.Payload[30] := $77; // w
  request.Payload[31] := $70; // p
  request.Payload[32] := $63; // c
  request.Payload[33] := $00; // Ende 2ter String
Mit diesem Query kommt aber nicht das an was ich angefordert habe.:(

BadenPower 25. Feb 2015 22:33

AW: Query an Gameserver
 
Wie ist denn request.Payload deklariert?

Denn wie in Post 52 kann es ja nicht sein:
Delphi-Quellcode:
TInfoRequest = packed record
  RequestHeader: Integer;
  PacketHeader: AnsiChar;
  Payload: array[0..19] of AnsiChar;
end;

Flodding 25. Feb 2015 22:39

AW: Query an Gameserver
 
Ich habe es abgeändert um die Querys wieder Byte für Byte eingeben zu können:
Delphi-Quellcode:
TInfoRequest = packed record
  RequestHeader: array[0..3] of Byte;
  PacketHeader: array[0..0] of Byte;
  Payload: array[0..40] of Byte;
end;
Die anderen beiden Querys habe ich dementsprechend umgebaut und die funktionieren weiterhin.

BadenPower 26. Feb 2015 09:39

AW: Query an Gameserver
 
Die Syntax aus #93 ist ok und gibt die "arma2arrowpc"-Server von "FF" = "Rest der Welt" (nicht weltweit) ordnungsgemäß zurück.


Mit der Deklaration aus #95 wirst Du allerdings an Grenzen stoßen, wenn Du es dem Benutzer ermöglichen willst, die Auslese-Parameter über Dein Programm zu ändern.

Flodding 26. Feb 2015 09:52

AW: Query an Gameserver
 
Ich habe dafür ja auch einen PHP-Vorlage:
Code:
<pre>
   <?php
   /*
      Example of how to get server list from steam. Works for both Arma 2 and 3 servers
      Original Source: https://developer.valvesoftware.com/wiki/User:Pizzahut/test.php
   */
   $master_servers = array("hl2master.steampowered.com");

   define("MIN_PORT", 27010); // Range of port numbers which the master servers
   define("MAX_PORT", 27013); // potentially use.

   // define("FILTER", '\gamedir\arma3'); // A3
   define("FILTER", '\gamedir\arma2arrowpc'); // A2
   define("REGION", "\xFF"); // region = world
   define("TIMEOUT", 2.0); // 2s timeout

   function query_timeout(&$socket, $seed)
   {
      echo "Sending query to master server.\n";
      stream_set_timeout($socket, TIMEOUT);
      if (!fwrite($socket, "1".REGION."$seed\0".FILTER."\0"))
      {
         fclose($socket);
         exit("fwrite error\n");
      }
      echo "Reading response header.\n";
      stream_set_timeout($socket, TIMEOUT);
      $s = bin2hex(fread($socket, 6));
      $info = stream_get_meta_data($socket);
      if ($info['timed_out'])
         echo "Master server timed out.\n";
      else
      {
         if ($s !== "ffffffff660a")
         {
            fclose($socket);
            if ($s == "")
               echo "Expected ff ff ff ff 66 0a (hex) but got nothing.\n";
            else
               echo "Expected ff ff ff ff 66 0a (hex) but got $s.\n";
            return True;
         }
      }
      return $info['timed_out'];
   }

   // Connect to master server, return timeout info.
   // The socket is passed as reference and thus returned as well.
   function master_server_timeout(&$socket, $ip)
   {
      $port = MIN_PORT;
      do {
         echo "Connecting to master server \"$ip:$port\".\n";
         $socket = fsockopen("udp://$ip", $port, $errno, $errstr, TIMEOUT);
         if (!$socket) exit("Error $errno : $errstr \n");
         $timeout = query_timeout($socket, "0.0.0.0:0");
         $port = $port + 1;
      } while ($timeout && ($port <= MAX_PORT));
      return $timeout;
   }
   // Repeat until list isn't empty.
   do
   {
      // Try all master servers until we find one that isn't timing out.
      do
         foreach ($master_servers as $ip)
           if ($timeout=master_server_timeout($socket, $ip))
             fclose($socket);
           else
             break;
      while ($timeout);
      // Read list with server addresses (IP:port).
      $count = 0;
      $old_a1 = 0; $old_a2 = 0; $old_a3 = 0; $old_a4 = 0; $old_a5 = 0;
      $max_timeouts = 6;
      do
      {
         stream_set_timeout($socket, TIMEOUT);
         $a1 = ord(fread($socket,1));
         $info = stream_get_meta_data($socket);
         if ($info['timed_out'])
         {
           $seed = "$old_a1.$old_a2.$old_a3.$old_a4:$old_a5";
           echo "Seed: $seed\n";
           while (query_timeout($socket, $seed));
           stream_set_timeout($socket, TIMEOUT);
           $a1 = ord(fread($socket,1));
           $info = stream_get_meta_data($socket);
           if ($info['timed_out'])
           {
             echo "Timeout occured.\n";
             break;
           }
           $check_for_duplicate = 1;
         }
         else
           $check_for_duplicate = 0;
         // Let's always read the rest of the address (even if it starts with 0) in
         // order to empty the master server's write buffer. This may avoid subsequent
         // problems, but I'm paranoid here.
         $a2 = ord(fread($socket,1));
         $a3 = ord(fread($socket,1));
         $a4 = ord(fread($socket,1));
         $a5 = ord(fread($socket,1))*256 + ord(fread($socket,1));
         if ($a1 != 0)
         {
           if (($check_for_duplicate==0)||($a1!=$old_a1)||($a2!=$old_a2)||
           ($a3!=$old_a3)||($a4!=$old_a4)||($a5!=$old_a5))
           {
            $count++;
            //echo "$count $a1.$a2.$a3.$a4:$a5\n";
            $addy = "$a1.$a2.$a3.$a4";
            $key = $addy.":".$a5;
            echo $key."\n";
           }
           $old_a1 = $a1; $old_a2 = $a2; $old_a3 = $a3; $old_a4 = $a4; $old_a5 = $a5;
         }
      } while ($a1 != 0);
      fclose($socket);
   } while ($count == 0);
   echo "Retrieved $count server addresses.\n";
?>
</pre>
Dort wird ja im oberen Teil Filter und Region definiert:
Code:
define("FILTER", '\gamedir\arma2arrowpc'); // A2
define("REGION", "\xFF"); // region = world
Wenn ich das Script ausführe:
http://last-templers.de/a2epoch/serverlist.php (dauert etwas das Laden)

Die Ausgabe ist die, die ich im Programm haben möchte.

BadenPower 26. Feb 2015 10:51

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1291584)
Die Ausgabe ist die, die ich im Programm haben möchte.

Und was geht denn ncht?


Die ersten 10 Server, welche ich über die Abfrage erhalte sind zum Beispiel:

84.191.11.101:2316
81.169.231.59:2313
85.214.238.7:27016
193.111.140.177:2311
146.0.42.124:2304
217.114.196.201:2301
85.190.173.146:2301
146.0.32.146:2301
178.254.34.206:2311
81.169.250.14:2301

Flodding 26. Feb 2015 12:03

AW: Query an Gameserver
 
So werte ich den Buffer aus:
Delphi-Quellcode:
    if chr(response.PacketHeader) = 'f' then
      begin
        for y := 0 to 10 do
        begin
        Move(buffer[i], response.First, SizeOf(response.First)); inc(i, SizeOf(response.First));
        Move(buffer[i], response.Second, SizeOf(response.Second)); inc(i, SizeOf(response.Second));
        Move(buffer[i], response.Third, SizeOf(response.Third)); inc(i, SizeOf(response.Third));
        Move(buffer[i], response.Fourth, SizeOf(response.Fourth)); inc(i, SizeOf(response.Fourth));
        Move(buffer[i], response.Port, SizeOf(response.Port)); inc(i, SizeOf(response.Port));
        Form1.ListBox1.Items.Add(IntToStr(response.First) + '.' + IntToStr(response.Second) + '.' + IntToStr(response.Third) + '.' + IntToStr(response.Fourth) + ':' + IntToStr(response.Port));
        Application.ProcessMessages;
        end;
      end;
    end;
Die 10 Server die bei mir rauskommen:

89.163.212.29:64776
89.163.202.2:34921
78.51.20.26:64776
91.64.197.127:34921
213.23.101.70:34921
134.255.218.121:53781
134.255.236.171:34921
134.255.217.20:34329
134.255.220.9:18460
134.255.217.200:24606

:?

BadenPower 26. Feb 2015 12:45

AW: Query an Gameserver
 
Deine Ports sind falsch.

Schau in Ruhe noch einmal das PHP-Beispiel an, welches Du selbst oben gepostet hast.

Wenn Du es dann noch nicht erkannt hast, dann gib nochmals bescheid.

Flodding 26. Feb 2015 15:51

AW: Query an Gameserver
 
Im PHP-Code wird IP : PORT wie folgt per "echo" ausgegeben:
Code:
$addy = "$a1.$a2.$a3.$a4";
$key = $addy.":".$a5;
echo $key."\n";
Einige Zeilen zuvor werden $a2 - $a5 gefüllt:
Code:
$a2 = ord(fread($socket,1));
$a3 = ord(fread($socket,1));
$a4 = ord(fread($socket,1));
$a5 = ord(fread($socket,1))*256 + ord(fread($socket,1));
$a1 wird vorher schon auf die selbe Weise gefüllt und es wird überprüft ob "$a1 = 0" bzw. in diesem Fall "$a1 != 0" ist.

Wenn ich jetzt den OBEREN Code richtig deute, dann ist "$a5" der Port.
Also betrachte ich mir folgende Zeile genauer:
Code:
$a5 = ord(fread($socket,1))*256 + ord(fread($socket,1));
Ich versuche den Code jetzt einfach mal zu übersetzen:
Variable "a5" ist gleich der ASCII-Wert des Binärwertes der empfangenen Daten multipliziert mit 256. Zu diesem wird dann nochmal der ASCII-Wert des Binärwertes von eben addiert.

Quellen:
http://php.net/manual/de/function.ord.php
http://php.net/manual/de/function.fread.php

Wenn ich jetzt nachschaue, was "Ord" in Delphi bedeutet:
http://www.delphibasics.co.uk/RTL.asp?Name=Ord

Dort lese ich heraus, dass ich mit "Ord" in Delphi einen "Integer"-Wert aus folgenden Typen bekomme: " AnsiChar | Char | WideChar | Enumeration | Integer "

Dann schaue ich mir mal meine Typ-Definition für den Port an:
Delphi-Quellcode:
TServerListResponse = record
  ResponseHeader: Integer;
  PacketHeader: Word;
  First: Byte; // Byte
  Second: Byte; // Byte
  Third: Byte; // Byte
  Fourth: Byte; // Byte
  Port: Word; // unsigned Short
  ServerAdresses: TserverAddresses;
end;
Laut diesen Tabellen ist der Typ "Word" in C gleich der Typ "unsigned int".
ganz unten:
http://edn.embarcadero.com/article/10156#H15

ca. in der mitte:
http://stackoverflow.com/questions/1...void-parameter

Ein Blick in die Protokol Doku von Valve:
https://developer.valvesoftware.com/...Query_Protocol

Port ist in C Typ "unsigned short" und das Equivalent in Delphi dazu ist ein Typ "Char".

Eigener Code abgeändert:
Delphi-Quellcode:
TServerListResponse = record
  ResponseHeader: Integer;
  PacketHeader: Word;
  First: Byte; // Byte
  Second: Byte; // Byte
  Third: Byte; // Byte
  Fourth: Byte; // Byte
  Port: Char; // unsigned Short
  ServerAdresses: TserverAddresses;
end;
Wenn ich jetzt den Serverquery ausführe:
Delphi-Quellcode:
    if chr(response.PacketHeader) = 'f' then
      begin
        for y := 0 to 10 do
        begin
        Move(buffer[i], response.First, SizeOf(response.First)); inc(i, SizeOf(response.First));
        Move(buffer[i], response.Second, SizeOf(response.Second)); inc(i, SizeOf(response.Second));
        Move(buffer[i], response.Third, SizeOf(response.Third)); inc(i, SizeOf(response.Third));
        Move(buffer[i], response.Fourth, SizeOf(response.Fourth)); inc(i, SizeOf(response.Fourth));
        Move(buffer[i], response.Port, SizeOf(response.Port)); inc(i, SizeOf(response.Port));
        Form1.ListBox1.Items.Add((IntToStr(response.First)) + '.' +
                                 (IntToStr(response.Second)) + '.' +
                                 (IntToStr(response.Third)) + '.' +
                                 (IntToStr(response.Fourth)) + ':' +
                                 response.Port );
        Application.ProcessMessages;
        end;
      end;
    end;
Das Ergebnis ist weniger befriedigend:

Code:
121.21.210.134:ÿ
Oh da ist ja das "ÿ" !! Das kenne ich doch vom vorherigen Query!! "ÿ" war doch die von HEX zu Text umgewandelte "FF". Also würde mir eine Umwandlung zu HEX schonmal nichts bringen.
Ach... sind wir ma experimentierfreudig:
Delphi-Quellcode:
char(response.Port) );
Ergebnis:
Code:
111.105.136.134:ÿ
Herrje !! :twisted:

Okay warte mal wir hatten doch auch in den vorherigen Querys "Shorts", "Bytes", "Strings" und "Words". Ersetzen wir mal die anderen Typen durch die in den gerade gefundenen Listen.

Ergebnis ist, dass mit den Typ-Definitionen von den Quellen die Querys falsche Werte zurückliefern.

Sind die Quellen falsch ? Lese ich sie einfach nur falsch?
Das kann doch nicht so schwer sein die Typen aus einer Tabelle zu übertragen und dann die jeweiligen Funktionen zur Umwandlung in einen String zur Ausgabe in einer Listbox zu finden.

Neutral General 26. Feb 2015 16:00

AW: Query an Gameserver
 
Ich verstehe nicht warum du jetzt den Port von Word (RICHTIG!) in Char umgewandelt hast..
Der Port ist wie immer eine Zahl und hat nix mit einem String zu tun.

Flodding 26. Feb 2015 16:16

AW: Query an Gameserver
 
Lese bitte die Tabelle am Ende dieser Seite:

http://edn.embarcadero.com/article/10156#H15

Laut dieser Tabelle ist ein "unsigned short" in C ein "Char" in Delphi.

Neutral General 26. Feb 2015 16:22

AW: Query an Gameserver
 
Laut mir ist es ein Word.
Die Tabelle von Embarcadero ist wenn nicht falsch zumindest veraltet oder/und sehr ungenau.

Flodding 26. Feb 2015 16:33

AW: Query an Gameserver
 
nagut als Word hab ichs ja auch nicht hinbekommen.

Sir Rufo 26. Feb 2015 16:37

AW: Query an Gameserver
 
Wen interessiert es hier, was ein "unsigned short" von C übersetzt nach Delphi bedeutet?

Es interessiert doch ausschliesslich was der "unsigned short" in der Schnittstelle bedeutet. Es ist ein 16bit Wert (little endian) und den kann man einfach so in ein
Delphi-Quellcode:
Word
oder auch
Delphi-Quellcode:
UInt16
einlesen.

Neutral General 26. Feb 2015 16:40

AW: Query an Gameserver
 
Wenn mich nicht alles täuscht kommt der Port als Big Endian an.
Du musst also die Bytereihenfolge tauschen nachdem du den Port ausgelesen hast.

Das geht z.B. mit dieser Funktion:

Delphi-Quellcode:
function SwapWord(W: Word): Word;
asm
  xchg al, ah
end;

// ....

Move(buffer[i], response.Port, SizeOf(response.Port)); inc(i, SizeOf(response.Port));
response.Port := SwapWord(response.Port);
Edit: Auch wenn mir das komisch erscheint weil alle anderen Werte doch als Little Endian ankamen oder nicht?
Aber laut
Code:
$a5 = ord(fread($socket,1))*256 + ord(fread($socket,1));
ist das erste Byte ja scheinbar das MSB, daher Big Endian.
(Das irritiert mich grad selbst etwas aber schau mal obs so funktioniert :gruebel:)

Sir Rufo 26. Feb 2015 16:58

AW: Query an Gameserver
 
Den PHP-Code kann man nur dann richtig interpretieren, wenn man weiß, wie dieser Ausdruck denn abgearbeitet wird.
PHP-Quellcode:
$a5 = ord(fread($socket,1))*256 + ord(fread($socket,1));
.

Ist die Ausführungs-Reihenfolge von rechts nach links, dann wird zuerst
PHP-Quellcode:
ord(fread($socket,1))
und dann
PHP-Quellcode:
ord(fread($socket,1))*256
ausgeführt. ;)

BadenPower 26. Feb 2015 17:27

AW: Query an Gameserver
 
Zitat:

Zitat von Neutral General (Beitrag 1291645)
Wenn mich nicht alles täuscht kommt der Port als Big Endian an.
Du musst also die Bytereihenfolge tauschen nachdem du den Port ausgelesen hast.

Das geht z.B. mit dieser Funktion:

Delphi-Quellcode:
function SwapWord(W: Word): Word;
asm
  xchg al, ah
end;

Du täuschst dich nicht.


Oder als direkte Übersetzung aus dem PHP-Code so:

Delphi-Quellcode:
          Move(lBuffer[lPos], lResponseServer.Fourth, SizeOf(lResponseServer.Fourth));
          Inc(lPos, SizeOf(lResponseServer.Fourth));
          //Move(lBuffer[lPos], lResponseServer.Port, SizeOf(lResponseServer.Port));
          lResponseServer.Port := lBuffer[lPos] * 256 + lBuffer[lPos+1];
          Inc(lPos, SizeOf(lResponseServer.Port));
Einfach nur eine Zeile im Quellcode geändert und schon kommt der richtige Port-Wert heraus.

Flodding 26. Feb 2015 17:36

AW: Query an Gameserver
 
Mit der Swapword-Funktion klappt es. Danke Neutral General

Ich muss jetzt aber dazu sagen, dass ich die ganze Zeit den Verdacht hatte, dass die Bytes vertauscht sind. Drauf gekommen bin ich durch das "unsigned" davor; bei den Anderen stand es nicht davor. Aber ich war fest davon überzeugt, dass das nicht so sein wird, da die anderen "Word" ja auch nicht vertauscht waren.

Ich hätt ma früher fragen sollen. Jetzt kanns ja jeder sagen.

Ich meld mich wieder wenn ich die komplette Serverliste selbst hinbekommen habe, und ja ich weiss keiner glaubt dran, oder wenn ich es nicht geschafft habe.:?

BadenPower 26. Feb 2015 18:27

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1291651)
Drauf gekommen bin ich durch das "unsigned" davor; bei den Anderen stand es nicht davor.

Das "unsigned" hat allerdings nichts damit zu tun, das sagt nur aus, ob der Wert auch ein Vorzeichen haben kann.

Da bei einer Port-Angabe es sinnlos wäre negative Werte zu ermöglichen ist der Wert als "unsigned" vorgegeben.


Dass die Bytes vertaucht sind, hätte Dir allerdings die Zuhilfename von Windows-Bordmitteln, wie z.B. dem Taschenrechner gezeigt. Dort hättest Du einfach einmal die Zahl, welche Du erhalten hast eingeben können und in Hex umwandeln lassen. Dann eifach die Hex-Werte vertauchen und zurück nach Dezimal umwandeln.

Wenn Du dann die erhaltene Zahl als Portadresse in Deinem Programm benutzt hättest. dann hättest Du gesehen, dass Daten empfangen werden.

Sir Rufo 26. Feb 2015 21:40

AW: Query an Gameserver
 
Liste der Anhänge anzeigen (Anzahl: 1)
Das die Daten vertauscht sind steht doch lang und breit in der Dokumentation:
Zitat:

Reply format
The reply always starts with FF FF FF FF 66 0A.
The format is then a series of these server address blocks:
TypeData
ByteFirst octet of IP address
ByteSecond octet of IP address
ByteThird octet of IP address
ByteFourth octet of IP address
Unsigned ShortPort number - usually 27015 (69 87) - this is network ordered, which is unlike every other Steam protocol.

Anyway, mit viel probieren bekommt man ja auch was heraus ... ok, Lesen geht manchmal schneller. Auch wenn man nicht versteht was network ordered ist, sollte der Hinweis which is unlike every other Steam protocol auf jeden Fall hellhörig machen. Da ist was anders als sonst (genau das steht da).

Da ich mir dieses Elend mit dem Auslesen und Schreiben nicht mehr ansehen kann, hier mal eine Unit, womit man dieses Pakete sehr einfach zusammenbauen und auch wieder auseinander nehmen kann.

Kleine Demo vorweg (alles was einem unbekannt vorkommt ist in der Unit
Delphi-Quellcode:
SourceQuery
definiert):
Delphi-Quellcode:
program SimpleTests;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  IdGlobal, IdUDPClient,
  StrUtils, SysUtils,
  SourceQuery in 'SourceQuery.pas';

procedure ParseInfoResponse( AResponse: TSourceQueryBytes );
var
  LID: SQUShort;
  LEDF: SQByte;
begin
  Writeln( 'Parse-Data:' );
  Writeln;
  Writeln( 'Protocol-Header ', AResponse.ReadLong );
  Writeln( 'Packet-Header  ', AResponse.ReadChar );
  Writeln( 'Protocol       ', AResponse.ReadByte );
  Writeln( 'Name           ', AResponse.ReadString );
  Writeln( 'Map            ', AResponse.ReadString );
  Writeln( 'Folder         ', AResponse.ReadString );
  Writeln( 'Game           ', AResponse.ReadString );
  LID := AResponse.ReadUShort;
  Writeln( 'ID             ', LID );
  Writeln( 'Players        ', AResponse.ReadByte );
  Writeln( 'Max. Players   ', AResponse.ReadByte );
  Writeln( 'Bots           ', AResponse.ReadByte );
  Writeln( 'Server Type    ', AResponse.ReadChar );
  Writeln( 'Environment    ', AResponse.ReadChar );
  Writeln( 'Visibility     ', AResponse.ReadByte );
  Writeln( 'VAC            ', AResponse.ReadByte );

  // Testen auf "The Ship"
  if ( LID >= 2400 ) and ( LID <= 2499 )
  then
    begin
      Writeln( 'Mode           ', AResponse.ReadByte );
      Writeln( 'Witnesses      ', AResponse.ReadByte );
      Writeln( 'Duration       ', AResponse.ReadByte );
    end;

  Writeln( 'Version        ', AResponse.ReadString );
  if not AResponse.Eof
  then
    begin
      LEDF := AResponse.ReadByte;
      Writeln( 'EDF            ', LEDF );
      if LEDF and $80 = $80
      then
        begin
          Writeln( 'Port           ', AResponse.ReadShort );
        end;
      if LEDF and $10 = $10
      then
        begin
          Writeln( 'SteamID        ', AResponse.ReadLongLong );
        end;
      if LEDF and $40 = $40
      then
        begin
          Writeln( 'Port           ', AResponse.ReadShort );
          Writeln( 'Name           ', AResponse.ReadString );
        end;
      if LEDF and $20 = $20
      then
        begin
          Writeln( 'Keywords       ', AResponse.ReadString );
        end;
      if LEDF and $01 = $01
      then
        begin
          Writeln( 'GameID         ', AResponse.ReadLongLong );
        end;
    end;
end;

procedure TestInfoResponse;
var
  LResponse: TSourceQueryBytes;
begin
  Writeln( 'Example response for Counter Strike: Source:' );
  Writeln;
  LResponse.SetData(
    {AData} HexStrToBytes(
      {AHexStr} StringReplace(
        {} 'FF FF FF FF 49 02 67 61 6D 65 32 78 73 2E 63 6F' +
        {} '6D 20 43 6F 75 6E 74 65 72 2D 53 74 72 69 6B 65' +
        {} '20 53 6F 75 72 63 65 20 23 31 00 64 65 5F 64 75' +
        {} '73 74 00 63 73 74 72 69 6B 65 00 43 6F 75 6E 74' +
        {} '65 72 2D 53 74 72 69 6B 65 3A 20 53 6F 75 72 63' +
        {} '65 00 F0 00 05 10 04 64 6C 00 00 31 2E 30 2E 30' +
        {} '2E 32 32 00',
        {OldPattern} ' ',
        {NewPattern} '',
        {Flags} [rfReplaceAll] ) ) );

  ParseInfoResponse( LResponse );
end;

procedure RealInfoRequest;
var
  LRequest, LResponse: TSourceQueryBytes;
  LUdp: TIdUDPClient;
  LBuffer: TIdBytes;
  LResponseSize: Integer;
begin

  // Prepare Request

  LRequest.WriteLong( SQ_SIMPLEPACKET_PROTOCOL_HEADER );
  LRequest.WriteByte( SQ_INFO_REQUEST_HEADER );
  LRequest.WriteString( 'Source Engine Query' );

  Writeln( 'REQUEST-DATA:' );
  Writeln;
  Writeln( LRequest.ToString );
  Writeln;

  LBuffer := LRequest.GetData;

  LUdp := TIdUDPClient.Create( nil );
  try

    // Send Request

    Write( 'Sending ... ' );
    LUdp.SendBuffer( '5.45.97.44', 2301, LBuffer );

    // Receive Response

    SetLength( LBuffer, SQ_PACKET_MAXSIZE );
    Write( 'Receiving ... ' );
    LResponseSize := LUdp.ReceiveBuffer( LBuffer );
    Writeln( LResponseSize, ' Bytes' );
    SetLength( LBuffer, LResponseSize );
    Writeln;
  finally
    LUdp.Free;
  end;

  Writeln( 'RESPONSE-DATA:' );
  Writeln;
  Writeln( DumpBuffer( LBuffer ) );
  Writeln;

  // Parse Response

  LResponse.SetData( LBuffer );

  ParseInfoResponse( LResponse );
end;

procedure RealMasterQueryRequest;
var
  LRequest, LResponse: TSourceQueryBytes;
  LBuffer: TIdBytes;
  LBufferSize: Integer;
  LUdp: TIdUDPClient;
  LServerAddress: TSQServerAddressBlock;
  LCount, LRetries: Integer;
begin

  LServerAddress.IP1 := 0;
  LServerAddress.IP2 := 0;
  LServerAddress.IP3 := 0;
  LServerAddress.IP4 := 0;
  LServerAddress.Port := 0;

  LUdp := TIdUDPClient.Create( nil );
  try

    LCount := 0;

    repeat // Daten abrufen ...

      LRequest.Clear;
      LRequest.WriteByte( $31 ); // Message Type
      LRequest.WriteByte( SQ_REGIONCODE_RESTOFTHEWORLD ); // Region Code
      LRequest.WriteString( LServerAddress.ToString ); // IP-Address
      LRequest.WriteString( '\gamedir\arma2arrowpc' ); // Filter

      LBuffer := LRequest.GetData;

      // Send Request

      LUdp.SendBuffer( 'hl2master.steampowered.com', 27011, LBuffer );

      LRetries := 0;
      repeat
        if LRetries >= 3
        then
          raise Exception.Create( 'Too many retries' );

        SetLength( LBuffer, SQ_PACKET_MAXSIZE );
        LBufferSize := LUdp.ReceiveBuffer( LBuffer, 2000 );
        Inc( LRetries );
      until LBufferSize > 0;
      SetLength( LBuffer, LBufferSize );

      LResponse.SetData( LBuffer );

      Assert( LResponse.ReadLong = SQ_SIMPLEPACKET_PROTOCOL_HEADER );
      Assert( LResponse.ReadByte = $66 );
      Assert( LResponse.ReadByte = $0A );

      // Parse the Server-Addresses
      while not LResponse.Eof do
        begin
          Inc( LCount );
          LServerAddress.IP1 := LResponse.ReadByte;
          LServerAddress.IP2 := LResponse.ReadByte;
          LServerAddress.IP3 := LResponse.ReadByte;
          LServerAddress.IP4 := LResponse.ReadByte;
          LServerAddress.Port := swap( LResponse.ReadUShort ); // swap weil die Doku das sagt

          Writeln( LCount:7, '. ', LServerAddress.ToString );
        end;

    until ( LServerAddress.ToString = '0.0.0.0:0' ); // ... bis die leere Adresse zurückkommt

  finally
    LUdp.Free;
  end;

end;

begin
  try
    TestInfoResponse;
    RealInfoRequest;
    RealMasterQueryRequest;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
Ausgabe von
Delphi-Quellcode:
RealInfoRequest
:
Code:
REQUEST-DATA:

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.

Sending ... Receiving ... 196 Bytes

RESPONSE-DATA:

FF FF FF FF 49 11 5B 4C 2D 54 2D 53 5D 20 45 70   ÿÿÿÿI.[L-T-S] Ep
6F 63 68 20 4F 72 69 67 69 6E 73 20 28 31 2E 30   och Origins (1.0
2E 35 2E 31 2F 31 32 35 35 34 38 29 00 54 61 76   .5.1/125548).Tav
69 00 61 72 6D 61 32 61 72 72 6F 77 70 63 00 44   i.arma2arrowpc.D
61 79 5A 20 45 70 6F 63 68 20 4F 72 69 67 69 6E   ayZ Epoch Origin
73 00 8A 84 01 19 00 64 77 00 00 31 2E 36 33 2E   s.??...dw..1.63.
31 32 35 35 34 38 00 B1 FE 08 01 CC 2C AF 75 14   125548.±þ..Ì,¯u.
40 01 62 74 2C 72 31 36 33 2C 6E 31 32 35 35 34   @.bt,r163,n12554
38 2C 73 37 2C 69 31 2C 6D 66 2C 6C 66 2C 76 66   8,s7,i1,mf,lf,vf
2C 64 74 2C 74 63 6F 6F 70 2C 67 36 35 35 34 35   ,dt,tcoop,g65545
2C 63 32 31 34 37 34 38 33 36 34 37 2D 32 31 34   ,c2147483647-214
37 34 38 33 36 34 37 2C 70 77 2C 00 8A 84 00 00   7483647,pw,.??..
00 00 00 00                                       ....

Parse-Data:

Protocol-Header -1
Packet-Header  I
Protocol       17
Name           [L-T-S] Epoch Origins (1.0.5.1/125548)
Map            Tavi
Folder         arma2arrowpc
Game           DayZ Epoch Origins
ID             33930
Players        1
Max. Players   25
Bots           0
Server Type    d
Environment    w
Visibility     0
VAC            0
Version        1.63.125548
EDF            177
Port           2302
SteamID        90094488230087681
Keywords       bt,r163,n125548,s7,i1,mf,lf,vf,dt,tcoop,g65545,c2147483647-21474
83647,pw,
GameID         33930
In der ganz kurzen Form (ohne Dump und Kommentare)) sieht ein Request dann wie folgt aus
Delphi-Quellcode:
procedure InfoRequest;
var
  LRequest, LResponse: TSourceQueryBytes;
  LUdp: TIdUDPClient;
  LBuffer: TIdBytes;
  LResponseSize: Integer;
begin
  LRequest.WriteLong( SQ_SIMPLEPACKET_PROTOCOL_HEADER );
  LRequest.WriteByte( SQ_INFO_REQUEST_HEADER );
  LRequest.WriteString( 'Source Engine Query' );

  LBuffer := LRequest.GetData;

  LUdp := TIdUDPClient.Create( nil );
  try
    LUdp.SendBuffer( '5.45.97.44', 2301, LBuffer );

    SetLength( LBuffer, SQ_PACKET_MAXSIZE );
    LResponseSize := LUdp.ReceiveBuffer( LBuffer );
    SetLength( LBuffer, LResponseSize );
  finally
    LUdp.Free;
  end;

  LResponse.SetData( LBuffer );

  ParseInfoResponse( LResponse );
end;
Der komplette Source im Anhang

BadenPower 28. Feb 2015 11:55

AW: Query an Gameserver
 
Zitat:

Zitat von Sir Rufo (Beitrag 1291673)
Da ich mir dieses Elend mit dem Auslesen und Schreiben nicht mehr ansehen kann, hier mal eine Unit, womit man dieses Pakete sehr einfach zusammenbauen und auch wieder auseinander nehmen kann.

Wow, da hat ja die Unit SouceQuery.pas, welche nur die Hilfsfunktionen enhält, schon fast so viele Zeilen Code, wie mein komplett funktionsfähiges Programm mit allen Parametereinstellmöglichkeiten für die Abfragen.


Da wundert es mich nicht mehr, dass viele Programme, welche nur ein Fenster haben zum Teil und das ohne Daten geladen zu haben, bereits 50kb im Hauptspeicher belegen, statt den eigentlich erforderlichen 4kb.

Sir Rufo 28. Feb 2015 14:57

AW: Query an Gameserver
 
Jeder wie er will von Spaghetticode bis ordentlich strukturiert. Irgendwo dazwischen sortiert man sich ein. Wo siehst du dich da (rhetorische Frage, es reicht wenn du dir die selber beantwortest).

Flodding 31. Okt 2015 20:46

AW: Query an Gameserver
 
...

Ich häng immernoch an der Kompletten Serverliste...

Ich bekomme stets nur den ersten Teil, danch bekomme ich nichts brauchbares...

Noch einer Lust mir zu helfen ?

MFG

Flo

Flodding 31. Okt 2015 22:44

AW: Query an Gameserver
 
Irgendwie hab ich meinen aktuellen Code vergessen:

Delphi-Quellcode:
procedure Get_SERVER_LIST(a1: byte; a2: byte; a3: byte; a4: byte; a5: Word);
var
//  request      : TA2S_InfoRequest;
  request      : TServerListRequest;
  response     : TServerListResponse;
  buffer       : TBufferArray;
  i            : Integer;
  y            : Integer;
  endoflist    : Boolean;
  endofpartlist : Boolean;

  new_a1        : byte;
  new_a2        : byte;
  new_a3        : byte;
  new_a4        : byte;
  new_a5        : word;

  old_a1        : byte;
  old_a2        : byte;
  old_a3        : byte;
  old_a4        : byte;
  old_a5        : word;

  old_seed         : String;
  new_seed         : String;

  newport_plain: Integer;
  oldport_plain: Integer;

begin

old_a1 := $30;
old_a2 := $30;
old_a3 := $30;
old_a4 := $30;
old_a5 := $3030;

// \gamedir\arma2arrowpc
// 31 FF 30 2e 30 2e 30 2e 30 3a 30 00 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 00

// \gamedir\arma3
// 31 ff 30 2e 30 2e 30 2e 30 3a 30 00 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 33

  request.RequestHeader := $31;
  request.PacketHeader := $FF;
  request.First        := $30;
  request.First_dot    := $2E;
  request.Second       := $30;
  request.Second_dot   := $2E;
  request.Third        := $30;
  request.Third_dot    := $2E;
  request.Fourth       := $30;
  request.double_dot   := $3A;
  request.Port         := $3030;
  request.Payload[0]   := $00;
  request.Payload[1]   := $5C;
  request.Payload[2]   := $67;
  request.Payload[3]   := $61;
  request.Payload[4]   := $6D;
  request.Payload[5]   := $65;
  request.Payload[6]   := $64;
  request.Payload[7]   := $69;
  request.Payload[8]   := $72;
  request.Payload[9]   := $5C;
  request.Payload[10]  := $61;
  request.Payload[11]  := $72;
  request.Payload[12]  := $6D;
  request.Payload[13]  := $61;
  request.Payload[14]  := $33;
  request.Payload[15]  := $00;


  Form1.udp1.BlockMode := bmNonBlocking;
  Form1.udp1.RemoteHost := 'hl2master.steampowered.com';
  Form1.udp1.RemotePort := '27011';

  if Form1.udp1.Active = false then
  begin
  form1.udp1.Active := true;
  form1.ListBox1.Clear;
  endoflist := false;
  end;

  if (Form1.udp1.Connected) then
    begin

    y := 0;
        repeat
               i := 0;
        form1.udp1.CleanupInstance;
        Form1.Udp1.SendBuf(request, SizeOf(request));
        Form1.udp1.WaitForData(250);
        Form1.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
        Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
        Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader));
//        form1.Caption := chr(response.ResponseHeader) + chr(response.PacketHeader) + IntToStr(i);
        form1.Memo1.Text := bintostr((buffer));
          repeat
            Move(buffer[i], response.First, SizeOf(response.First)); inc(i, SizeOf(response.First));
            Move(buffer[i], response.Second, SizeOf(response.Second)); inc(i, SizeOf(response.Second));
            Move(buffer[i], response.Third, SizeOf(response.Third)); inc(i, SizeOf(response.Third));
            Move(buffer[i], response.Fourth, SizeOf(response.Fourth)); inc(i, SizeOf(response.Fourth));
//            Response.Port := Buffer[i] * 256 + Buffer[i+1]; Inc(i, SizeOf(Response.Port)); // Weil die Bytes vertauscht sind, müssen sie umgedreht werden für ein richtiges
            newport_plain := Buffer[i] * 256 + Buffer[i+1]; Inc(i, SizeOf(Response.Port)); // Weil die Bytes vertauscht sind, müssen sie umgedreht werden für ein richtiges

            new_a1 := response.First;
            new_a2 := response.Second;
            new_a3 := response.Third;
            new_a4 := response.Fourth;
            new_a5 := response.Port;

            new_seed := IntToStr(new_a1) + IntToStr(new_a2) + IntToStr(new_a3) + IntToStr(new_a4) + IntToStr(new_a5);

        if (response.First <> 00) AND (response.Second <> 00) AND (response.Third <> 00) AND (response.Fourth <> 00) then
              begin
                form1.duplicatcounter := 0;

               Form1.ListBox1.Items.Add ((IntToStr(new_a1)) + '.' +
                                          (IntToStr(new_a2)) + '.' +
                                          (IntToStr(new_a3)) + '.' +
                                          (IntToStr(new_a4)) + ':' +
                                           IntToStr((newport_plain)) );

                old_a1 := response.First;
                old_a2 := response.Second;
                old_a3 := response.Third;
                old_a4 := response.Fourth;
                old_a5 := newport_plain;
                old_seed := IntToStr(old_a1) + IntToStr(old_a2) + IntToStr(old_a3) + IntToStr(old_a4) + IntToStr(newport_plain);

                Application.ProcessMessages;
              end
            else
              begin
                Form1.duplicatcounter := form1.duplicatcounter + 1;
                request.First  := strtoint( StringToHex(inttostr(old_a1)));
                request.Second := strtoint( StringToHex(inttostr(old_a2)));
                request.Third  := strtoint( StringToHex(inttostr(old_a3)));
                request.Fourth := strtoint( StringToHex(inttostr(old_a4)));
                request.Port   := strtoint( StringToHex(inttostr(old_a5)));
                Form1.Edit3.Text := StringToHex(inttostr(old_a1) + inttostr(old_a2) + inttostr(old_a3) + inttostr(old_a4) + inttostr(old_a5)) ;
              end;
              y := y + 1;
              form1.Caption := inttostr(y);
//          until (IntToStr(response.First)) + '.' + (IntToStr(response.Second)) + '.' + (IntToStr(response.Third)) + '.' + (IntToStr(response.Fourth)) + ':' + IntToStr((response.Port)) = '0.0.0.0:0';
          until form1.duplicatcounter = 2;

        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('NewSeed: ' + new_seed);
        Form1.ListBox1.Items.Add ('OldSeed: ' + old_seed);
        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('---------------');

        until y > 500;

      end;

Form1.udp1.Active := false;
form1.Caption := 'Servers: ' + inttostr(Form1.ListBox1.Items.Count -1);
end;
irgendwie bekomme ich 4 mal die selbe liste.

p80286 1. Nov 2015 10:38

AW: Query an Gameserver
 
Zitat:

Zitat von Flodding (Beitrag 1320281)
irgendwie bekomme ich 4 mal die selbe liste.

Wenn Du 4 mal die selbe Liste Bekommst, könnte es sein, daß du 4 mal den gleichen Bufferinhalt liest.
Darum würde ich zunächst einmal den Buffer vor versenden des Request nullen
Delphi-Quellcode:
fillchar
wenn beim zweiten lesen nur Nullen kommen, scheint die Gegenstelle keine Daten geliefert zu haben.

Delphi-Quellcode:
request.First  := strtoint( StringToHex(inttostr(old_a1)));
Und wofür ist dieses seltsame Konstrukt gut?

Gruß
K-H

Flodding 1. Nov 2015 10:50

AW: Query an Gameserver
 
Danke für den Hinweis mit dem Leeren des Buffers. Leider hat dies noch nicht zum Erfolg geführt.

Delphi-Quellcode:
FillChar(buffer, SizeOf(buffer), 0);
Nachdem ich das ganze ein wenig überarbeitet habe, komme ich nun wenigstens schonmal zu einem sicheren Ergebnis. Ich bekomme immer exakt 2 Mal die selbe Liste (geplant -> y - Vaiable) obwohl ich die IP Adresse im "response" ändere.

Hier erstmal der Code:
Delphi-Quellcode:
procedure Get_SERVER_LIST();
var
  request      : TServerListRequest;
  response     : TServerListResponse;
  buffer       : TBufferArray;
  i            : Integer;
  y            : Integer;

  new_a1        : byte;
  new_a2        : byte;
  new_a3        : byte;
  new_a4        : byte;
  new_a5        : word;

  old_a1        : byte;
  old_a2        : byte;
  old_a3        : byte;
  old_a4        : byte;
  old_a5        : word;

  old_seed         : String;
  new_seed         : String;

begin

old_a1 := 30;
old_a2 := 30;
old_a3 := 30;
old_a4 := 30;
old_a5 := 3030;

// \gamedir\arma2arrowpc
// 31 FF 30 2e 30 2e 30 2e 30 3a 30 00 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 32 61 72 72 6f 77 70 63 00

// \gamedir\arma3
// 31 ff 30 2e 30 2e 30 2e 30 3a 30 00 5c 67 61 6d 65 64 69 72 5c 61 72 6d 61 33

  Form1.udp1.BlockMode := bmNonBlocking;
  Form1.udp1.RemoteHost := 'hl2master.steampowered.com';
  Form1.udp1.RemotePort := '27011';
  form1.udp1.Open;
  y := 0;

  form1.ListBox1.Clear;

  if (Form1.udp1.Connected) then
    begin
        repeat
          request.RequestHeader := $31;
          request.PacketHeader := $FF;
          request.First        := old_a1;
          request.First_dot    := $2E;
          request.Second       := old_a2;
          request.Second_dot   := $2E;
          request.Third        := old_a3;
          request.Third_dot    := $2E;
          request.Fourth       := old_a4;
          request.double_dot   := $3A;
          request.Port         := old_a5;
          request.Payload[0]   := $00;
          request.Payload[1]   := $5C;
          request.Payload[2]   := $67;
          request.Payload[3]   := $61;
          request.Payload[4]   := $6D;
          request.Payload[5]   := $65;
          request.Payload[6]   := $64;
          request.Payload[7]   := $69;
          request.Payload[8]   := $72;
          request.Payload[9]   := $5C;
          request.Payload[10]  := $61;
          request.Payload[11]  := $72;
          request.Payload[12]  := $6D;
          request.Payload[13]  := $61;
          request.Payload[14]  := $33;
          request.Payload[15]  := $00;

        Form1.Udp1.SendBuf(request, SizeOf(request));
        Form1.udp1.WaitForData(250);
        Form1.Udp1.ReceiveBuf(buffer, SizeOf(buffer));
        i := 0;
        Move(buffer[i], response.ResponseHeader, SizeOf(response.ResponseHeader)); inc(i, SizeOf(response.ResponseHeader));
        Move(buffer[i], response.PacketHeader, SizeOf(response.PacketHeader)); inc(i, SizeOf(response.PacketHeader));
          repeat
            Move(buffer[i], response.First, SizeOf(response.First)); inc(i, SizeOf(response.First));
            Move(buffer[i], response.Second, SizeOf(response.Second)); inc(i, SizeOf(response.Second));
            Move(buffer[i], response.Third, SizeOf(response.Third)); inc(i, SizeOf(response.Third));
            Move(buffer[i], response.Fourth, SizeOf(response.Fourth)); inc(i, SizeOf(response.Fourth));
            Response.Port := Buffer[i] * 256 + Buffer[i+1]; Inc(i, SizeOf(Response.Port)); // Weil die Bytes vertauscht sind, müssen sie umgedreht werden für ein richtiges

            new_a1 := response.First;
            new_a2 := response.Second;
            new_a3 := response.Third;
            new_a4 := response.Fourth;
            new_a5 := response.Port;

            new_seed := IntToStr(new_a1) + IntToStr(new_a2) + IntToStr(new_a3) + IntToStr(new_a4) + IntToStr(new_a5);

            if (new_seed <> '00000') then
              begin
                 form1.duplicatcounter := 0;
                 Form1.ListBox1.Items.Add ((IntToStr(new_a1)) + '.' + (IntToStr(new_a2)) + '.' + (IntToStr(new_a3)) + '.' + (IntToStr(new_a4)) + ':' + IntToStr((new_a5)) );
                 old_a1 := new_a1;
                 old_a2 := new_a2;
                 old_a3 := new_a3;
                 old_a4 := new_a4;
                 old_a5 := new_a5;
                 old_seed := IntToStr(old_a1) + IntToStr(old_a2) + IntToStr(old_a3) + IntToStr(old_a4) + IntToStr(new_a5);
                 Application.ProcessMessages;
              end
            else
              begin
                Form1.duplicatcounter := form1.duplicatcounter + 1;
              end;
          until form1.duplicatcounter = 1;
        y := y + 1;
        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('NewSeed: ' + new_seed);
        Form1.ListBox1.Items.Add ('OldSeed: ' + old_seed);
        Form1.ListBox1.Items.Add ('---------------');
        Form1.ListBox1.Items.Add ('---------------');
        FillChar(Buffer, SizeOf(buffer), 0);  // Buffer leeren
        until y = 2;
      end;
Form1.udp1.Close;
form1.Caption := 'Servers: ' + inttostr(Form1.ListBox1.Items.Count -1);
end;
Mir scheint ich hab immernoch ein Problem damit die empfangenen Bytes für die IP und das Word für den Port richtig zu übergeben.

:oops::cry:


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:42 Uhr.
Seite 3 von 3     123   

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