Author Topic: REFRESH Command  (Read 24074 times)

0 Members and 1 Guest are viewing this topic.

Offline EnEsCe

  • Retired Soldat Developer
  • Flamebow Warrior
  • ******
  • Posts: 3101
  • http://enesce.com/
    • [eC] Official Website
REFRESH Command
« on: June 09, 2006, 01:23:22 am »
(Repost from old SoldatForums)
The following snippets of code can be used to parse the results of the 'REFRESH' packet which is used by remote admin clients to get live server stats such as Players, scores, map, time etc etc.

Pascal/Delphi Code (by Michal Marcinkowski)
Code: [Select]
const
PLAYERNAME_CHARS = 24;

TMsg_Refresh = packed record
Name : array[1..32] of string[PLAYERNAME_CHARS];
Team : array[1..32] of byte;
Kills : array[1..32] of word;
Deaths : array[1..32] of word;
Ping : array[1..32] of byte;
Number : array[1..32] of byte;
IP : array[1..32,1..4] of byte;
TeamScore : array[1..4] of word;
MapName : string[16];
TimeLimit, CurrentTime : integer;
KillLimit : word;
GameStyle : byte;
end;

C/C++ Code (by ramirez)
Another one(by ramirez)
Code: [Select]
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;

#pragma pack(push, 1)
struct RefreshPacket {
struct {
byte iLen;
char cName[24];
} sName[32];
byte iTeam[32];
word iKills[32];
word iDeaths[32];
byte iPing[32];
byte iNumber[32];
union {
dword iLong;
byte iPiece[4];
} uIp[32];
word iTeamScore[4];
struct {
byte iLen;
char cMap[16];
} sMap;
dword iTimeLimit;
dword iCurrentTime;
word iKillLimit;
byte iGameStyle;
};
#pragma pack(pop)

Visual Basic Code (by Chrisgbk)
Code: [Select]
Private Type RefreshPacket
    PlayerName(31) As String
    Team(31) As Byte
    Kills(31) As Long
    Deaths(31) As Long
    Ping(31) As Byte
    Number(31) As Byte
    IP(31, 3) As Byte
    TeamScore(3) As Long
    MapName As String
    TimeLimit As Double
    CurrentTime  As Double
    KillLimit As Long
    GameStyle As Byte
End Type

Private Function parseRefreshPacket(packet() As Byte, Optional offset As Byte = 0) As RefreshPacket
    Dim i As Integer
    Dim k As Integer
    Dim j As Integer
    Dim length As Byte
    i = offset
    With parseRefreshPacket
        For k = 0 To 31
            length = packet(i)
            i = i + 1
            For j = 0 To 23
                .PlayerName(k) = .PlayerName(k) + Chr(packet(i))
                i = i + 1
            Next j
            .PlayerName(k) = Left(.PlayerName(k), length)
        Next k
        For k = 0 To 31
           .Team(k) = packet(i)
            i = i + 1
        Next k
        For k = 0 To 31
            .Kills(k) = packet(i) + (packet(i + 1) * 256&)
            i = i + 2
        Next k
        For k = 0 To 31
            .Deaths(k) = packet(i) + (packet(i + 1) * 256&)
            i = i + 2
        Next k
        For k = 0 To 31
            .Ping(k) = packet(i)
            i = i + 1
        Next k
        For k = 0 To 31
            .Number(k) = packet(i)
            i = i + 1
        Next k
        For k = 0 To 31
            For j = 0 To 3
                .IP(k, j) = packet(i)
                i = i + 1
            Next j
        Next k
        For k = 0 To 3
            .TeamScore(k) = packet(i) + (packet(i + 1) * 256&)
            i = i + 2
        Next k
        length = packet(i)
        i = i + 1
        For k = 0 To 15
            .MapName = .MapName + Chr(packet(i))
            i = i + 1
        Next k
        .MapName = Left(.MapName, length)
        For k = 0 To 3
            .TimeLimit = .TimeLimit + (packet(i) * (256& ^ k))
            i = i + 1
        Next k
        For k = 0 To 3
            .CurrentTime = .CurrentTime + (packet(i) * (256& ^ k))
            i = i + 1
        Next k
        .KillLimit = packet(i) + (packet(i + 1) * 256&)
        i = i + 2
        .GameStyle = packet(i)
    End With
End Function

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    Static refresh As Boolean
    Dim tempstring As String
    Dim Buffer() As Byte
    dim packet as RefreshPacket
    Winsock1.GetData Buffer
    tempstring = StrConv(Buffer, vbUnicode)
    If Left(tempstring, 7) = "REFRESH" Then
        If Len(tempstring) = 9 Then
            refresh = True
        Else
            packet = parseRefreshPacket(Buffer, 9)
        End If
    ElseIf refresh And UBound(Buffer) = 1187 Then
        packet = parseRefreshPacket(Buffer)
        refresh = False
    Else
        Text1.Text = Text1.Text & tempstring
    End If
   
    Text1.SelStart = Len(Text1.Text)
    'todo: output the refresh packet data somewhere
End Sub

PHP
Scroll Down


mIRC Script
Scroll Down

Offline FliesLikeABrick

  • Administrator
  • Flamebow Warrior
  • *****
  • Posts: 6144
    • Ultimate 13 Soldat
Re: REFRESH Command
« Reply #1 on: June 09, 2006, 07:18:20 am »
I believe someone also made a parser for it in php.  Let me see if myself or mar77a can find it

Offline Yukwunhang

  • Major
  • *
  • Posts: 70
Re: REFRESH Command
« Reply #2 on: June 09, 2006, 08:25:17 am »
PHP (By ramirez)
Code: [Select]
<?php
function 
GetSoldatInfo(&$sock) {
if (!$sock) return false;
$info = array(
'gamemode' => 0,
'teammode' => false,
'pointmode' => false,
'players'  => 0,
'spectators'  => 0,
'map' => '',
'timelimit' => 0,
'currenttime' => 0,
'timeleft' => 0,
'limit' => 0,
'player' => array(),
'spectator' => array(),
'team' => array()
);

// Temporary array for players
$players = array();
for ($i 0$i 32$i++) {
$players[$i] = array(
'name'  => '',
'ip'  => '',
'id'  => 0,
'kills' => 0,
'deaths' => 0,
'team' => 0,
'ping' => 0
);
}

// Get player names
for ($i 0$i 32$i++) {
$data fread($sock25);
$len ord($data[0]);
$players[$i]['name'] = substr($data1$len);
}

// Get player teams
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['team'] = ord($data);
}

// Get player kills
for ($i 0$i 32$i++) {
$data unpack("v"fread($sock2));
$kills $data[1];
$players[$i]['kills'] = $kills;
}

// Get player deaths
for ($i 0$i 32$i++) {
$data unpack("v"fread($sock2));
$deaths $data[1];
$players[$i]['deaths'] = $deaths;
}

// Get player pings
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['ping'] = ord($data);
}

// Get player IDs
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['id'] = ord($data);
}

// Get player IPs
for ($i 0$i 32$i++) {
$data unpack("N"fread($sock4));
$players[$i]['ip'] = long2ip($data[1]);
}

// Get team scores
for ($i 1$i 5$i++) {
$data unpack("v"fread($sock2));
$score $data[1];
$info['team'][$i] = $score;
}

// Get map name
$data fread($sock17);
$len ord($data[0]);
$info['map'] = substr($data1$len);

// Get time limit & current time, and form timeleft
$data unpack("V"fread($sock4));
$timelimit $data[1];
$info['timelimit'] = $timelimit;
$data unpack("V"fread($sock4));
$currenttime $data[1];
$info['currenttime'] = $timelimit $currenttime;

$timeleft $currenttime;
$temp = (floor($timeleft 60) % 60);
$info['timeleft'] = floor($timeleft 3600) . ':' . ($temp 10 '0' '') . $temp;

// Get kill limit
$data unpack("v"fread($sock2));
$limit $data[1];
$info['limit'] = $limit;

// Get gamestyle
$data fread($sock1);
$gamestyle ord($data);
$info['gamemode'] = $gamestyle;
if ($gamestyle == || $gamestyle == || $gamestyle == || $gamestyle == 6) {
$info['teammode'] = true;
if ($gamestyle != 2) { $info['pointmode'] = true; }
}
if ($gamestyle != 2) {
if ($gamestyle != && $gamestyle != 5) {
unset($info['team'][1]);
unset($info['team'][2]);
}
unset($info['team'][3]);
unset($info['team'][4]);
}

foreach ($players as $player) {
if ($player['team'] < 5) {
$info['players']++;
$info['player'][] = $player;
}
else if ($player['team'] == 5) {
$info['spectators']++;
$info['spectator'][] = $player;
}
}

return $info;
}
?>




Example usage:

<?php
$s = @fsockopen("localhost"23073);
if ($s) {
$info = array();
fputs($s"password\n");
fputs($s"REFRESH\n");
while(1) {
$data trim(fgets($s1024));
if (!$data) break;
else if ($data == "REFRESH") {
$info GetSoldatInfo($s);
break;
}
}
fclose($s);

// Make sure the REFRESH packet was received
if ($info) {
// You can now use the $info array to check different stuff from server, check the function for the array's elements
// Remember that the gamemode value is a number, so to get the string do something like:
$gamemodes = array(
=> 'Deathmatch',
=> 'Pointmatch',
=> 'Teammatch',
=> 'Capture The Flag',
=> 'Rambomatch',
=> 'Infiltration',
=> 'Hold The Flag'
);
$gamemode_str $gamemodes[$info['gamemode']];
}
}
?>




And another note, the $info['timeleft'] is actually a string, in form of mins:secs, because I only needed to use it in this form. If you need an integral value in seconds, or ticks, it's easy enough to edit to have it in that form.
« Last Edit: June 09, 2006, 08:27:42 am by Yukwunhang »

Offline ramirez

  • Retired Soldat Developer
  • Camper
  • ******
  • Posts: 394
    • Soldat Central
Re: REFRESH Command
« Reply #3 on: June 11, 2006, 11:58:56 am »
Here's one for mIRC:

mIRC Scripting Language (by ramirez)
Code: [Select]
alias -l SoldatRefreshEndTok return $+($1, $iif($1 != $null, $chr($3)), $2)
alias SoldatRefreshSize return 1188
alias SoldatRefresh {
  var %id = $1, %binvar = $2, %table = $3, %args = $4-, %players = $null, %kills = $null, %deaths = $null, %ips = $null, %pings = $null, %numbers = $null, %offsets = $null, %teams = $null, %i, %offset = 1, %tplayers = 0, %tspectators = 0, %teamnames = Alpha|Bravo|Charlie|Delta|Spectator
  if (%id == $null || %table == $null || $bvar(%binvar, 0) != $SoldatRefreshSize) { return $null  }

  hfree -w %table $+ _*
  hmake %table $+ _info 7

  ; Get player names
  %i = 1
  while (%i <= 32) {
    var %len = $bvar(%binvar, %offset)
    inc %offset 1
    %players = $SoldatRefreshEndTok(%players, $bvar(%binvar, %offset, %len).text, 10)
    %offsets = $SoldatRefreshEndTok(%offsets, $calc(%i - 1), 10)
    inc %offset 24
    inc %i 1
  }

  ; Get player teams
  %i = 1
  var %deleted = 0
  while (%i <= 32) {
    var %team = $bvar(%binvar, %offset)
    inc %offset 1
    if (%team >= 0 && %team <= 5) {
      if (%team != 5) { inc %tplayers 1 }
      else { inc %tspectators 1 }
      %teams = $instok(%teams, %team, $calc($numtok(%teams, 10) + 1), 10)
    }
    else {
      %players = $deltok(%players, $calc(%i - %deleted), 10)
      %offsets = $deltok(%offsets, $calc(%i - %deleted), 10)
      inc %deleted 1
    }
    inc %i 1
  }

  var %total = %tplayers + %tspectators

  ; Get kills
  %i = 1
  while (%i <= %total) {
    %kills = $SoldatRefreshEndTok(%kills, $bvar(%binvar, $calc(%offset + $gettok(%offsets, %i, 10) * 2)).word, 10)
    inc %i 1
  }
  inc %offset $calc(32 * 2)

  ; Get deaths
  %i = 1
  while (%i <= %total) {
    %deaths = $SoldatRefreshEndTok(%deaths, $bvar(%binvar, $calc(%offset + $gettok(%offsets, %i, 10) * 2)).word, 10)
    inc %i 1
  }
  inc %offset $calc(32 * 2)

  ; Get ping
  %i = 1
  while (%i <= %total) {
    %pings = $SoldatRefreshEndTok(%pings, $bvar(%binvar, $calc(%offset + $gettok(%offsets, %i, 10))), 10)
    inc %i 1
  }
  inc %offset $calc(32)

  ; Get number
  %i = 1
  while (%i <= %total) {
    %numbers = $SoldatRefreshEndTok(%numbers, $bvar(%binvar, $calc(%offset + $gettok(%offsets, %i, 10))), 10)
    inc %i 1
  }
  inc %offset $calc(32)

  ; Get IPs
  %i = 1
  while (%i <= %total) {
    %ips = $SoldatRefreshEndTok(%ips, $bvar(%binvar, $calc(%offset + $gettok(%offsets, %i, 10) * 4)).nlong, 10)
    inc %i 1
  }
  inc %offset $calc(32 * 4)

  %i = 1
  while (%i <= %tplayers) {
    hmake %table $+ _player $+ %i 7
    inc %i
  }
  %i = 1
  while (%i <= %tspectators) {
    hmake %table $+ _spectator $+ %i 7
    inc %i
  }
  hadd %table $+ _info players %tplayers
  hadd %table $+ _info spectators %tspectators

  %i = 1
  var %i_plr = 1, %i_spec = 1
  while (%i <= %total) {
    var %team = $gettok(%teams, 1, 10)
    if (%team < 5) {
      hadd %table $+ _player $+ %i_plr name $gettok(%players, 1, 10)
      hadd %table $+ _player $+ %i_plr team $gettok(%teamnames, %team, 124)
      hadd %table $+ _player $+ %i_plr kills $gettok(%kills, 1, 10)
      hadd %table $+ _player $+ %i_plr deaths $gettok(%deaths, 1, 10)
      hadd %table $+ _player $+ %i_plr ping $gettok(%pings, 1, 10)
      hadd %table $+ _player $+ %i_plr number $gettok(%numbers, 1, 10)
      hadd %table $+ _player $+ %i_plr ip $gettok(%ips, 1, 10)
      inc %i_plr
    }
    else {
      hadd %table $+ _spectator $+ %i_spec name $gettok(%players, 1, 10)
      hadd %table $+ _spectator $+ %i_spec ping $gettok(%pings, 1, 10)
      hadd %table $+ _spectator $+ %i_spec number $gettok(%numbers, 1, 10)
      hadd %table $+ _spectator $+ %i_spec ip $gettok(%ips, 1, 10)
      inc %i_spec
    }
    %players = $deltok(%players, 1, 10)
    %teams = $deltok(%teams, 1, 10)
    %kills = $deltok(%kills, 1, 10)
    %deaths = $deltok(%deaths, 1, 10)
    %pings = $deltok(%pings, 1, 10)
    %numbers = $deltok(%numbers, 1, 10)
    %ips = $deltok(%ips, 1, 10)
    inc %i 1
  }

  hmake %table $+ _teams 4

  ; Get team scores
  %i = 1
  while (%i <= 4) {
    var %team = $gettok(%teamnames, %i, 124)
    var %score = $bvar(%binvar, %offset).word
    inc %offset 2
    hadd %table $+ _teams %team %score
    inc %i 1
  }

  ; Get map name
  var %len = $bvar(%binvar, %offset)
  inc %offset 1
  var %map = $bvar(%binvar, %offset, %len).text
  inc %offset 16
  hadd %table $+ _info map %map

  ; Get time limit
  var %timelimit = $bvar(%binvar, %offset).long
  inc %offset 4
  hadd %table $+ _info timelimit $calc(%timelimit / 3600) $+ :00

  ; Get time left
  var %timeleft = $bvar(%binvar, %offset).long
  inc %offset 4
  var %temp = $calc($floor($calc(%timeleft / 60)) % 60)
  hadd %table $+ _info timeleft $floor($calc(%timeleft / 3600)) $+ : $+ $iif(%temp < 10, 0 $+ %temp, %temp)

  ; Get kill limit
  var %killlimit = $bvar(%binvar, %offset).word
  inc %offset 2
  hadd %table $+ _info killlimit %killlimit

  ; Get gamestyle
  var %gamestyle = $bvar(%binvar, %offset)
  hadd %table $+ _info gamestyle %gamestyle

  .signal %id %table %args
}

Here's a better one for C++ (or rather, easier to use since it does more than just parses):
C++ (by ramirez)
http://www.avalanchestudios.net/ramirez/soldatinfo.h
Remotely linked because it's pretty long.

Offline Jones

  • Major(1)
  • Posts: 3
Re: REFRESH Command
« Reply #4 on: September 26, 2006, 11:27:01 am »
The PHP code is not clean so I added some lines.
PHP
Code: [Select]
<?php
function 
GetSoldatInfo(&$sock) {
if (!$sock) return false;
$info = array(
'gamemode' => 0,
'teammode' => false,
'pointmode' => false,
'players'  => 0,
'spectators'  => 0,
'map' => '',
'timelimit' => 0,
'currenttime' => 0,
'timeleft' => 0,
'limit' => 0,
'player' => array(),
'spectator' => array(),
'team' => array()
);

                
//Set player and spectator count to zero
       $info['players'] = 0;
       $info['spectators'] = 0;

// Temporary array for players
$players = array();
for ($i 0$i 32$i++) {
$players[$i] = array(
'name'  => '',
'ip'  => '',
'id'  => 0,
'kills' => 0,
'deaths' => 0,
'team' => 0,
'ping' => 0
);
}

// Get player names
for ($i 0$i 32$i++) {
$data fread($sock25);
$len ord($data[0]);
$players[$i]['name'] = substr($data1$len);
}

// Get player teams
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['team'] = ord($data);
}

// Get player kills
for ($i 0$i 32$i++) {
$data unpack("v"fread($sock2));
$kills $data[1];
$players[$i]['kills'] = $kills;
}

// Get player deaths
for ($i 0$i 32$i++) {
$data unpack("v"fread($sock2));
$deaths $data[1];
$players[$i]['deaths'] = $deaths;
}

// Get player pings
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['ping'] = ord($data);
}

// Get player IDs
for ($i 0$i 32$i++) {
$data fread($sock1);
$players[$i]['id'] = ord($data);
}

// Get player IPs
for ($i 0$i 32$i++) {
$data unpack("N"fread($sock4));
$players[$i]['ip'] = long2ip($data[1]);
}

// Get team scores
for ($i 1$i 5$i++) {
$data unpack("v"fread($sock2));
$score $data[1];
$info['team'][$i] = $score;
}

// Get map name
$data fread($sock17);
$len ord($data[0]);
$info['map'] = substr($data1$len);

// Get time limit & current time, and form timeleft
$data unpack("V"fread($sock4));
$timelimit $data[1];
$info['timelimit'] = $timelimit;
$data unpack("V"fread($sock4));
$currenttime $data[1];
$info['currenttime'] = $timelimit $currenttime;

$timeleft $currenttime;
$temp = (floor($timeleft 60) % 60);
$info['timeleft'] = floor($timeleft 3600) . ':' . ($temp 10 '0' '') . $temp;

// Get kill limit
$data unpack("v"fread($sock2));
$limit $data[1];
$info['limit'] = $limit;

// Get gamestyle
$data fread($sock1);
$gamestyle ord($data);
$info['gamemode'] = $gamestyle;
if ($gamestyle == || $gamestyle == || $gamestyle == || $gamestyle == 6) {
$info['teammode'] = true;
if ($gamestyle != 2) { $info['pointmode'] = true; }
}
if ($gamestyle != 2) {
if ($gamestyle != && $gamestyle != 5) {
unset($info['team'][1]);
unset($info['team'][2]);
}
unset($info['team'][3]);
unset($info['team'][4]);
}

foreach ($players as $player) {
if ($player['team'] < 5) {
$info['players']++;
$info['player'][] = $player;
}
else if ($player['team'] == 5) {
$info['spectators']++;
$info['spectator'][] = $player;
}
}

return $info;
}
?>


Added lines:
Code: [Select]
//Set player and spectator count to zero
$info['players'] = 0;
$info['spectators'] = 0;

If I'll find more mistakes I will post it.

Jones, the kiddy who is sorry about his bad English ;)

Offline ramirez

  • Retired Soldat Developer
  • Camper
  • ******
  • Posts: 394
    • Soldat Central
Re: REFRESH Command
« Reply #5 on: September 26, 2006, 02:50:54 pm »
... Try to look at the code better next time before making changes. Notably see these two lines?
Code: [Select]
'players' => 0,
'spectators' => 0,
They were set to 0 before your 'fix'.

Offline Jones

  • Major(1)
  • Posts: 3
Re: REFRESH Command
« Reply #6 on: September 26, 2006, 03:02:29 pm »
I'm sorry.

Offline EnEsCe

  • Retired Soldat Developer
  • Flamebow Warrior
  • ******
  • Posts: 3101
  • http://enesce.com/
    • [eC] Official Website
Re: REFRESH Command
« Reply #7 on: September 26, 2006, 08:40:35 pm »
Why isnt this stickied...

Offline DeMo

  • Soldier
  • **
  • Posts: 127
  • Stay Metal! \m/
    • Encoder 2002
Re: REFRESH Command
« Reply #8 on: October 02, 2006, 07:01:45 pm »
What about REFRESHX?
Can someone please post the structure for it?

Thanks.

<@Evil-Ville> Expect a picture of Chakra` holding his fleshlight soon!

Offline EnEsCe

  • Retired Soldat Developer
  • Flamebow Warrior
  • ******
  • Posts: 3101
  • http://enesce.com/
    • [eC] Official Website
Re: REFRESH Command
« Reply #9 on: October 02, 2006, 07:49:51 pm »
REFRESHX isnt completed yet, if it gets changed (and it will), all programs that use it will become unusable

Offline DeMo

  • Soldier
  • **
  • Posts: 127
  • Stay Metal! \m/
    • Encoder 2002
Re: REFRESH Command
« Reply #10 on: October 03, 2006, 12:03:47 pm »
I understand your point Enesce, I'm just curious to experiment with it a little. ;)

<@Evil-Ville> Expect a picture of Chakra` holding his fleshlight soon!

Offline chrisgbk

  • Inactive Staff
  • Veteran
  • *****
  • Posts: 1739
Re: REFRESH Command
« Reply #11 on: October 14, 2006, 12:18:12 pm »
Ruby REFRESH parsing.

Code: [Select]
socket = TCPsocket.open(server, port)
socket.puts(password)

while data=socket.gets.strip
  if data =~ /REFRESH/
    refresh = ""
    socket.read(1188, refresh)
    info = refresh.unpack("CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24CA24C32S32S32C32C32C32C32C32C32S4CA16l2SC")      names = Array.new(32, "")
      teams = Array.new(32, 0)
      kills = Array.new(32, 0)
      deaths = Array.new(32, 0)
      pings = Array.new(32, 0)
      numbers = Array.new(32, 0)
      ips =Array.new(32, Array.new(4, 0))
      teamscore = Array.new(4, 0)
      mapname = ""
      timelimit = 0
      currenttime = 0
      killlimit = 0
      gamestyle = 0
      32.times do |i|
        names[i] = info[2*i+1].to_s[0, info[2*i]]
        teams[i] = info[i+64]
        kills[i] = info[i+96]
        deaths[i] = info[i+128]
        pings[i] = info[i+160]
        numbers[i] = info[i+192]
        4.times do |j|
          ips[i][j] = info[(i-1)*4+j+224]
        end
      end
      4.times do |i|
        teamscore[i] = info[i+352]
      end
      mapname = info[357][0,info[356]]
      timelimit = info[358]
      currenttime = info[359]
      killlimit = info[360]
      gamestyle = info[361]
  end
end
« Last Edit: January 11, 2007, 03:15:20 pm by chrisgbk »

Offline ramirez

  • Retired Soldat Developer
  • Camper
  • ******
  • Posts: 394
    • Soldat Central
Re: REFRESH Command
« Reply #12 on: February 21, 2007, 08:08:35 pm »
TCL REFRESH parser:

Code: [Select]
proc Soldat:ParseRefresh packet {
if { [string length $packet] != 1188 } {
return
}

binary scan $packet "@800c32s32s32c32c32I32s4ca16iisc" teams kills deaths pings numbers ips scores map_len map timelimit timeleft limit gamemode

set players {}
set specs {}

for { set i 0 } { $i < 32 } { incr i } {
set team [lindex $teams $i]
if {$team != -1} {
binary scan $packet "@[expr $i*25]ca24" name_len name
set ip [lindex $ips $i]
set ip [join [list [expr ($ip >> 24) & 0xFF] [expr ($ip >> 16) & 0xFF] [expr ($ip >> 8) & 0xFF] [expr $ip & 0xFF]] .]

array set player [list id      [expr [lindex $numbers $i] & 0xFF] \
name    [string range $name 0 [expr $name_len-1]] \
team    $team \
kills   [expr [lindex $kills $i] & 0xFFFF] \
deaths  [expr [lindex $deaths $i] & 0xFFFF] \
ping    [expr [lindex $pings $i] & 0xFF] \
ip      $ip]

if {$team < 5} {
lappend players [array get player]
} else {
lappend specs [array get player]
}
}
}

for { set i 0 } { $i < 4 } { incr i } {
set scores [lreplace $scores $i $i [expr [lindex $scores $i] & 0xFFFF]]
}

array set info [list gamemode    $gamemode \
num_players [llength $players] \
num_specs   [llength $specs] \
map         [string range $map 0 [expr $map_len-1]] \
scores      $scores \
limit       $limit \
timelimit   [expr $timelimit / 3600] \
timeleft    [expr $timeleft / 60] \
players     $players \
specs       $specs]

return [array get info]
}

This could use a dict instead, but since it's only standard starting from 8.5, and not everyone has the dict package for 8.4 I decided to use arrays instead. After you've read the refresh packet from a socket (eg. to $refresh variable), this is how it should be parsed:
Code: [Select]
array set info [Soldat:ParseRefresh $refresh]You can then use $info(gamemode) etc. To access players you do:
Code: [Select]
foreach player $info(players) {
array set pinfo $player
set name $pinfo(name)
}
etc.
« Last Edit: October 12, 2007, 11:17:22 am by ramirez »

Offline ManSoft|Warlord

  • Major
  • *
  • Posts: 70
  • Soldat.IDE Creator
Re: REFRESH Command
« Reply #13 on: February 27, 2007, 01:19:26 pm »
has someone already did s.th. like that for JAVA?

Offline GofT

  • Major(1)
  • Posts: 20
Re: REFRESH Command
« Reply #14 on: March 09, 2007, 12:46:02 pm »
O.o

i love coding, trying to learn some all the time, but...
WOW
do you hardcore soldat players have a life?
dont mean this as an insult...
but wow, i must say, you guys really are people who love computers...

Offline Quantifier

  • Major
  • *
  • Posts: 70
Re: REFRESH Command
« Reply #15 on: April 28, 2007, 05:40:28 pm »
Wow, Tcl parser, nice. But there is a small mistake, for loop should go to 32. Also, instead of:
Code: [Select]
array set player [list id      [expr [lindex $numbers $i] & 0xFF]]
array set player [list name    [string range $name 0 [expr $name_len-1]]]
array set player [list team    $team]
array set player [list kills   [expr [lindex $kills $i] & 0xFFFF]]
array set player [list deaths  [expr [lindex $deaths $i] & 0xFFFF]]
array set player [list ping    [expr [lindex $pings $i] & 0xFF]]
array set player [list ip      $ip]
...
set players [linsert $players end [array get player]]
..
set scores [lreplace $scores $i $i [expr [lindex $scores $i] & 0xFFFF]]
you can use
Code: [Select]
array set player [list id      [expr [lindex $numbers $i] & 0xFF] \
        name    [string range $name 0 [expr $name_len-1]] \
        team    $team \
        kills   [expr [lindex $kills $i] & 0xFFFF] \
        deaths  [expr [lindex $deaths $i] & 0xFFFF] \
        ping    [expr [lindex $pings $i] & 0xFF] \
        ip      $ip]
...
lappend players [array get player]
...
lset scores $i [expr [lindex $scores $i] & 0xFFFF]

Offline ramirez

  • Retired Soldat Developer
  • Camper
  • ******
  • Posts: 394
    • Soldat Central
Re: REFRESH Command
« Reply #16 on: April 30, 2007, 02:48:11 am »
Wow, Tcl parser, nice. But there is a small mistake, for loop should go to 32. Also, instead of:
Oops, I must've been very tired. Fixed that, thanks for correcting it!

Quote
Also, instead of:
Code: [Select]
array set player [list id      [expr [lindex $numbers $i] & 0xFF]]
array set player [list name    [string range $name 0 [expr $name_len-1]]]
array set player [list team    $team]
array set player [list kills   [expr [lindex $kills $i] & 0xFFFF]]
array set player [list deaths  [expr [lindex $deaths $i] & 0xFFFF]]
array set player [list ping    [expr [lindex $pings $i] & 0xFF]]
array set player [list ip      $ip]
...
set players [linsert $players end [array get player]]
..
set scores [lreplace $scores $i $i [expr [lindex $scores $i] & 0xFFFF]]
you can use
Code: [Select]
array set player [list id      [expr [lindex $numbers $i] & 0xFF]
        name    [string range $name 0 [expr $name_len-1]]
        team    $team
        kills   [expr [lindex $kills $i] & 0xFFFF]
        deaths  [expr [lindex $deaths $i] & 0xFFFF]
        ping    [expr [lindex $pings $i] & 0xFF]
        ip      $ip]
...
lappend players [array get player]
...
lset scores $i [expr [lindex $scores $i] & 0xFFFF]
Yeah, you're right about the array set and lappend thing, should've done it that way. As for using lset, the command was introduced in TCL later than other list related commands, and I wanted to keep the script as much backward compatible as possible. The server I host my eggdrop on, for example, doesn't have lset available (and since I'm not an admin of the server, I can't exactly update the TCL interpreter either).

Date Posted: April 29, 2007, 02:58:20 AM
Oh and here's an improved parser for PHP that can also parse REFRESHX packets. Keep in mind though that REFRESHX can change since it isn't final, so you'd better stick with REFRESH unless you're doing something experimental and want to try it.

If you want to download instead:
http://www.soldatcentral.net/soldat.refresh.phps

Code: [Select]
<?php
 
/*
*  SOLDAT REFRESH AND REFRESHX PARSER
* By: ramirez (c) 2007
*
*  Usage:
* $info = ParseRefresh($packet);
*   or
* $info = ParseRefreshX($packet);
*
*  Example:
*
* <?php
*
* include('soldat.refresh.php');
*
* $socket = fsockopen('your.server.com', 23073);
*
* fputs($socket, "PASSWORD\n");
* fputs($socket, "REFRESH\n");
* fputs($socket, "REFRESHX\n");
*
* $version = null;
* while ($data = trim(fgets($socket, 1024))) {
* if (preg_match('/^Server Version: (.+)$/', $data, $match)) {
* $version = $match[1];
* }
* else if ($data == 'REFRESH') {
* $info = ParseRefresh(fread($socket, REFRESH_PACKET_SIZE), $version);
* print_r($info);
* }
* else if ($data == 'REFRESHX') {
* $info = ParseRefresh(fread($socket, RefreshXSize($version)), $version);
* print_r($info);
* break;
* }
* }
*
* fclose($socket);
*
* ?>

*
*/
 
define('REFRESH_PACKET_SIZE',  1188);
 
function RefreshXSize($version = '2.6.5')
{
if ($version >= '2.6.5') return 1608;
return 1576;
}
 
function ParseRefresh(&$packet, $version = '2.6.5') {
if (strlen($packet) == REFRESH_PACKET_SIZE) {
$refreshx = false;
}
else if (strlen($packet) == RefreshXSize($version)) {
$refreshx = true;
}
else {
return false;
}
 
$info = array(
'gamemode'    => 0,
'players'     => 0,
'spectators'  => 0,
'map'         => '',
'timelimit'   => 0,
'currenttime' => 0,
'timeleft'    => 0,
'limit'       => 0,
'player'      => array(),
'spectator'   => array(),
'team'        => array()
);
 
if ($refreshx) {
$info = array_merge($info, array(
'maxplayers' => 0,
'maxspecs'   => 0,
'nextmap'    => '',
'passworded' => false,
'redflag'    => array(),
'blueflag'   => array()
));
}
 
$players = array();
for ($i = 0; $i < 32; $i++) {
$players[$i] = array(
'name'   => '',
'ip'     => '',
'id'     => 0,
'kills'  => 0,
'caps'   => 0,
'deaths' => 0,
'team'   => 0,
'ping'   => 0
);
 
if ($refreshx) {
$players[$i] = array_merge($players[$i], array(
'x' => 0,
'y' => 0
));
}
}
 
$pos = 800;
 
$teams                            = unpack('C*', substr($packet, $pos, 32));  $pos += 32;
$kills                            = unpack('v*', substr($packet, $pos, 64));  $pos += 64;
if ($version >= '2.6.5') {  $caps = unpack('C*', substr($packet, $pos, 32));  $pos += 32;  }
$deaths                           = unpack('v*', substr($packet, $pos, 64));  $pos += 64;
if (!$refreshx)          { $pings = unpack('C*', substr($packet, $pos, 32));  $pos += 32;  }
else                     { $pings = unpack('l*', substr($packet, $pos, 128)); $pos += 128; }
$ids                              = unpack('C*', substr($packet, $pos, 32));  $pos += 32;
$ips                              = unpack('N*', substr($packet, $pos, 128)); $pos += 128;
if ($refreshx)           { $locs  = unpack('f*', substr($packet, $pos, 256)); $pos += 256; }
 
for ($i = 0; $i < 32; $i++) {
$players[$i]['name']   = substr($packet, $i*25+1, ord($packet[$i*25]));
$players[$i]['team']   = $teams[$i+1];
$players[$i]['kills']  = $kills[$i+1];
$players[$i]['caps']   = $caps[$i+1];
$players[$i]['deaths'] = $deaths[$i+1];
$players[$i]['ping']   = $pings[$i+1];
$players[$i]['id']     = $ids[$i+1];
$players[$i]['ip']     = long2ip($ips[$i+1]);
 
if ($refreshx) {
$players[$i]['x'] = $locs[$i+1];
$players[$i]['y'] = $locs[$i+33];
}
}
 
if ($refreshx) {
$data = unpack('f*', substr($packet, $pos, 16)); $pos += 16;
$info['redflag']  = array('x' => $data[1], 'y' => $data[2]);
$info['blueflag'] = array('x' => $data[3], 'y' => $data[4]);
}
 
$teams = unpack('v*', substr($packet, $pos, 8)); $pos += 8;
$map   = unpack('Clen/A16name', substr($packet, $pos, 17)); $pos += 17;
$time  = unpack('V*', substr($packet, $pos, 8)); $pos += 8;
$limit = unpack('v', substr($packet, $pos, 2)); $pos += 2;
 
$timelimit = $time[1];
$currenttime = $time[2];
$timeleft = $currenttime;
$temp = (floor($timeleft / 60) % 60);
$info['timeleft'] = floor($timeleft / 3600) . ':' . ($temp < 10 ? '0' : '') . $temp;
 
$info['team'] = $teams;
$info['map'] = substr($map['name'], 0, $map['len']);
$info['timelimit'] = $timelimit;
$info['currenttime'] = $timelimit - $currenttime;
$info['limit'] = $limit[1];
$info['gamemode'] = ord($packet[$pos++]);
 
if ($refreshx) {
$data = unpack('C*', substr($packet, $pos, 4)); $pos += 4;
$info['maxplayers'] = $data[1];
$info['maxspecs']   = $data[2];
$info['passworded'] = ($data[3] != 0 ? true : false);
$info['nextmap']    = substr($packet, $pos, $data[4]);
}
 
if ($info['gamemode'] != 2) {
if ($info['gamemode'] != 3 && $info['gamemode'] != 5) {
unset($info['team'][1]);
unset($info['team'][2]);
}
unset($info['team'][3]);
unset($info['team'][4]);
}
 
foreach ($players as $player) {
if ($player['team'] < 5) {
$info['players']++;
$info['player'][] = $player;
}
else if ($player['team'] == 5) {
$info['spectators']++;
$info['spectator'][] = $player;
}
}
 
return $info;
}
 
?>
« Last Edit: May 21, 2009, 02:25:27 pm by ramirez »

Offline Flippeh

  • Major
  • *
  • Posts: 58
  • Fish go moo :>
    • My Homepage
Re: REFRESH Command
« Reply #17 on: August 13, 2007, 04:14:22 pm »
BUMP!

Did someone do this for C# or VB.NET?
I tried for looooong enough to build one, but i don't get a foothold.
Managed to get the playernames (buggy, of course)..

Someone made a parser for .NET?
Or can someone?
Or anyone who can help me writing one?
I apprectiate any help!

This is what i have come up with so far
« Last Edit: August 13, 2007, 04:25:25 pm by Flippeh »

Offline chrisgbk

  • Inactive Staff
  • Veteran
  • *****
  • Posts: 1739
Re: REFRESH Command
« Reply #18 on: August 13, 2007, 07:27:03 pm »
When the server replies to REFRESH, it puts REFRESH before the data, so you should loop until you get a line that is REFRESH, then parse the data. Your code looks like it will read the first name correctly, but it needs to consume more bytes after it reads the name because of padding.

Something along the lines of(but probably not exactly because I'm doing this quickly off the top of my head):

for (int j = strLen; j < 24; j++)
{
     br.ReadChar();
}

Offline Flippeh

  • Major
  • *
  • Posts: 58
  • Fish go moo :>
    • My Homepage
Re: REFRESH Command
« Reply #19 on: November 06, 2007, 05:41:09 pm »
Okay, improved the code a bit: http://rafb.net/p/PmCZuH74.html
Still need to figure out how to continue...

If someone wants to contact me, i am on IRC all the time (using the same nick as here).
:o

Date Posted: 14 August 2007, 23:43:56
Bump!
I just made a "parser" for the D programming language.
I merely looked at the existing C++ parser, and changed the datatypes to fit to D, and some other stuffs

Code: [Select]
align(1) struct Server
{
  align(1) struct Playername
  {
    byte len;
    char[24] name;
  }

  align(1) union IP
  {
    uint ilong;
    byte piece[4];
  }

  align(1) struct Map
  {
    byte len;
    char[16] map;
  }

  Playername[32] name;
  ubyte[32] team;
  ushort[32] kills;
  ushort[32] deaths;
  ubyte[32] ping;
  ubyte[32] number;
  IP[32] ip;
 
  ushort teamscore[4];
  Map map;
  uint timelimit;
  uint currenttime;
  ushort killlimit;
  byte gamestyle; 
}

I'm not going to post a whole programm thats working, but the snippet to load the refresh packet into the struct.

Code: [Select]
Server info;
stream.read(fd); // Comment this out if you don't use stream.readLine
stream.readBlock(&info, info.sizeof);

Now its still not being perfect, because the map name is padded with \000 to the total length of 16.

Use this..
Code: [Select]
info.map.map[0..info.map.len];to get it without padding. Same goes for the Playernames.

This is probably useless for anyone else but me (i don't think anyone else is coding in D here.. what a waste!), but it was fun to build, and i learned while finishing it =)
« Last Edit: November 06, 2007, 05:43:56 pm by Flippeh »