Author Topic: REFRESH Command  (Read 26402 times)

0 Members and 1 Guest are viewing this topic.

Offline data_smith

  • Major(1)
  • Posts: 1
Re: REFRESH Command
« Reply #20 on: May 09, 2008, 01:22:20 am »
I wrote a REFRESH-packet parser for C#.
You can get a copy
here: http://lemonserv.com/pub/REFRESHParser/source

Code: [Select]
using System;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using System.Net.Sockets;
using System.Net;

                                                   
    ////////////////////////////////////////////   
    //                                        //   
    //   #### 2oo8 A LemonSoft Product ####   //   
    //                                        //   
    ////////////////////////////////////////////   
                                                   
/*                                                 
 * As of 07.05.2008                                 
 * This sourcecode is licenced under GNU GPL.       
 * To use this you MUST include original           
 * programmers name (Iivari Mokelainen @ LemonSoft)
 *                                                 
 * For thanks, questions, suggestions and help     
 * for problems email me to                         
 *                                                 
 *     data_smith[at]hotmail[dot]com               
 *                                                 
 * Thank you!                                       
 *                                                 
 */                                                 


namespace LemonSoft
{
    public class REFRESHEx
    {
        public SoldatPlayer[] Players = new SoldatPlayer[MaxPlayers];
        public ushort[] TeamScores = new ushort[4];
        public string MapName = string.Empty;
        public TimeSpan TimeLimit, CurrentTime;
        public ushort KillLimit = 0;
        public SoldatGameStyle GameStyle = 0;
        public const int MaxPlayers = 32;
        public static REFRESHEx REFRESHFromBytes(byte[] bytes, int offset)
        {
            REFRESHEx result = new REFRESHEx();
            //NAMES
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Name = Encoding.Default.GetString(bytes, offset + 1, bytes[offset]);
                offset += 25;
            }
            //TEAM
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Team = bytes[offset];
                offset++;
            }
            //KILLS
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Kills = BitConverter.ToUInt16(bytes, offset);
                offset += 2;
            }
            //DEATHS
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Deaths = BitConverter.ToUInt16(bytes, offset);
                offset += 2;
            }
            //PING
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Ping = bytes[offset];
                offset++;
            }
            //NUMBER
            for (int i = 0; i < MaxPlayers; i++)
            {
                result.Players[i].Number = bytes[offset];
                offset++;
            }
            //IP
            for (int i = 0; i < MaxPlayers; i++)
            {
                byte[] bits = new byte[4];
                Array.Copy(bytes, offset, bits, 0, 4);
                result.Players[i].IP = new IPAddress(bits);
                offset += 4;
            }
            //TeamScores
            result.TeamScores[0] = BitConverter.ToUInt16(bytes, offset); offset += 2;
            result.TeamScores[1] = BitConverter.ToUInt16(bytes, offset); offset += 2;
            result.TeamScores[2] = BitConverter.ToUInt16(bytes, offset); offset += 2;
            result.TeamScores[3] = BitConverter.ToUInt16(bytes, offset); offset += 2;
            //MapName
            result.MapName = Encoding.Default.GetString(bytes, offset + 1, bytes[offset]);
            offset += 17;
            //Time
            result.TimeLimit = TimeSpan.FromSeconds(BitConverter.ToUInt32(bytes, offset) / 60d); offset += 4;
            result.CurrentTime = TimeSpan.FromSeconds(BitConverter.ToUInt32(bytes, offset) / 60); offset += 4;
            //KillLimit
            result.KillLimit = BitConverter.ToUInt16(bytes, offset); offset += 2;
            //GameStyle
            result.GameStyle = (SoldatGameStyle)bytes[offset];
            return result;
        }

        public static void GetREFRESHSocket(out byte[] buffer, out int received, Socket socket)
        {

        ReTry:
            ;
            long startTime = DateTime.Now.Ticks;
            received = 0;
            buffer = new byte[10000];
            socket.Send(Encoding.Default.GetBytes("REFRESH\n"));
            while (received != 1188 && received != 1197)
            {
                Array.Clear(buffer, 0, buffer.Length);
                if ((DateTime.Now.Ticks - startTime) / TimeSpan.TicksPerMillisecond > 2000)
                {
                    //Console.WriteLine("Fail, retrying");
                    goto ReTry;
                }
                received = socket.Receive(buffer);
            }

        }

        public static REFRESHEx Get(Socket socket)
        {
            byte[] buffer;
            int received;
            GetREFRESHSocket(out buffer, out received, socket);
           
            return REFRESHFromBytes(buffer, received - 1188);
        }
        public int GetTeamPlayers(int team)
        {
            int count = 0;
            foreach (SoldatPlayer player in Players)
            {
                if (player.Team == team) count++;
                if (team == -1) count++;
            }
            return count;
        }
    }
    public enum SoldatGameStyle : byte
    {
        DM = 0,
        PM = 1,
        TM = 2,
        CTF = 3,
        Rambo = 4,
        INF = 5,
        HTF = 6
    }
    public struct SoldatPlayer
    {
        public string Name;
        public byte Team, Ping, Number;
        public ushort Kills, Deaths;
        public IPAddress IP;
    }
}

Usage: just pass a connected and logged in socket to  the REFRESHEx.Get() function.

for ex.
Code: [Select]
REFRESHEx r = REFRESHEx.Get(ConnectedSocket);
« Last Edit: May 09, 2008, 01:38:52 am by data_smith »

Offline Rape Is Funny

  • Major(1)
  • Posts: 47
  • Hey Juliet
Re: REFRESH Command
« Reply #21 on: June 23, 2008, 10:02:59 pm »
I also did one in C#.

Code: [Select]
        public class RefreshPacket
        {   
            public string[] PlayerName = new string[32];
            public byte[] Team = new byte[32];
            public long[] Kills = new long[32];
            public long[] Deaths = new long[32];
            public byte[] Ping = new byte[32];
            public byte[] Number = new byte[32];
            public byte[,] IP = new byte[32, 4];
            public long[] TeamScore = new long[4];
            public string MapName;
            public long TimeLimit;
            public long CurrentTime;
            public long KillLimit;
            public byte GameStyle;
        }
 
        private RefreshPacket parseRefreshPacket(byte[] packet, int offset)
        {
            int i = offset;
            byte length;
            RefreshPacket tempPacket = new RefreshPacket();
 
            for (int k = 0; k < 32; k++)
            {
                length = packet[i];
                i++;
                for (int j = 0; j < 24; j++)
                {
                    tempPacket.PlayerName[k] += Convert.ToChar(packet[i]);
                    i++;
                }
                tempPacket.PlayerName[k] = left(tempPacket.PlayerName[k], length);
            }
            for (int k = 0; k < 32; k++)
            {
                tempPacket.Team[k] = packet[i];
                i++;
            }
            for (int k = 0; k < 32; k++)
            {
                tempPacket.Kills[k] = packet[i] + (packet[i + 1] * 256);
                i += 2;
            }
            for (int k = 0; k < 32; k++)
            {
                tempPacket.Deaths[k] = packet[i] + (packet[i + 1] * 256);
                i += 2;
            }
            for (int k = 0; k < 32; k++)
            {
                tempPacket.Ping[k] = packet[i];
                i++;
            }
            for (int k = 0; k < 32; k++)
            {
                tempPacket.Number[k] = packet[i];
                i++;
            }
            for (int k = 0; k < 32; k++)
            {
                for (int j = 0; j < 4; j++)
                {
                    tempPacket.IP[k, j] = packet[i];
                    i++;
                }
            }
            for (int k = 0; k < 4; k++)
            {
                tempPacket.TeamScore[k] = packet[i] + (packet[i + 1] * 256);
                i += 2;
            }
            length = packet[i];
            i++;
            for (int k = 0; k < 16; k++)
            {
                tempPacket.MapName += Convert.ToChar(packet[i]);
                i++;
            }
            tempPacket.MapName = left(tempPacket.MapName, length);
            for (int k = 0; k < 4; k++)
            {
                //You can use getTime() to return the time limit as a string in "MM:SS" format.
                tempPacket.TimeLimit += Convert.ToInt64(packet[i] * (Math.Pow(256, k)));
                i++;
            }
            for (int k = 0; k < 4; k++)
            {
                //You can use getTime() to return the time left as a string in "MM:SS" format.
                tempPacket.CurrentTime += Convert.ToInt64(packet[i] * (Math.Pow(256, k)));
                i++;
            }
            tempPacket.KillLimit = packet[i] + (packet[i + 1] * 256);
            i += 2;
            tempPacket.GameStyle = packet[i];
 
 
            return tempPacket;
        }
 
 
        private string left(string word, int x)
        {
            if (x <= word.Length)
                word = word.Remove(x);
            return word;
        }
 
        private string getTime(long ticks)
        {
            string time;
            time = getMinutes(ticks) + ':' + getSeconds(ticks);
            return time;
        }
 
        private string getMinutes(long ticks)
        {
            int x;
            string minutes;
            x = ((Convert.ToInt32(ticks) / 60) / 60);
            minutes = Convert.ToString(x);
            return minutes;
        }
 
        private string getSeconds(long ticks)
        {
            int x;
            string seconds;
            x = ((Convert.ToInt32(ticks) / 60) % 60);
            seconds = Convert.ToString(x);
            if (seconds.Length == 1)
                seconds = '0' + seconds;
            return seconds;
        }

Offline Rape Is Funny

  • Major(1)
  • Posts: 47
  • Hey Juliet
Re: REFRESH Command
« Reply #22 on: June 28, 2008, 08:07:56 pm »
Mine is better to use: Player class and gives you times in TimeSpan not in ticks which you have to GetTime()  :)

oh and btw, i found out much more efficient way to read the binary data.. like extremely fast and easy to use.. (dig in BinaryReader - way)

and kill limit is really a ushort=uint16=16 bits=2bytes not and long=int64=64bits=8bytes

Ok dood, congratulations, I made this in 1 day, the first day I ever tried C#. It works, so I really don't care.

Also, GetTime() works just fine.

Offline Rape Is Funny

  • Major(1)
  • Posts: 47
  • Hey Juliet
Re: REFRESH Command
« Reply #23 on: June 30, 2008, 01:57:24 am »
I'm making a remote admin client. :P

Offline EnEsCe

  • Retired Soldat Developer
  • Flamebow Warrior
  • ******
  • Posts: 3101
  • http://enesce.com/
    • [eC] Official Website
Re: REFRESH Command
« Reply #24 on: June 30, 2008, 05:33:50 am »
Make a new topic if you want to discuss. In case you haven't noticed, this topic is for posting code snippets to parse REFRESH. Any replies after this must follow suit or they will be deleted and user warned for offtopic spam.

Offline iDante

  • Veteran
  • *****
  • Posts: 1967
Re: REFRESH Command
« Reply #25 on: August 03, 2008, 04:17:47 pm »
Java REFRESH parser (by me and one little bit from SARJ, by rusty (though I could have done that my way, it was much faster to just adapt his code into mine (even though his is a little confusing at first look))):
Code: [Select]
String[] Names = new String[32];
int[] Teams = new int[32];
int[] Kills = new int[32];
int[] Deaths = new int[32];
int[] Ping = new int[32];
int[] Number = new int[32];
String[] IP = new String[32];
int[] TeamScore = new int[4];
String MapName;
int TimeLimit;
int CurrentTime;
int KillLimit;
int GameScoreLimit;
int GameStyle;
String GameType;
int tempNameLen;

public void parseRefresh(String RefreshMsg) {
if(RefreshMsg.length() >=1188) {
RefreshMsg = RefreshMsg.substring(9);

// Names
for(int i=0;i<32;i++) {
tempNameLen = (int)RefreshMsg.charAt(0);
Names[i] = new String();
for(int p=1;p<=tempNameLen;p++) {
Names[i] += RefreshMsg.charAt(p);
}
RefreshMsg = RefreshMsg.substring(25);
}
// Teams
for(int i=0;i<32;i++) {
Teams[i] = (int) RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(1);
}
// Kills
for(int i=0;i<32;i++) {
Kills[i] = (int) RefreshMsg.charAt(0);
Kills[i] |= (int) RefreshMsg.charAt(1) << 8;
RefreshMsg = RefreshMsg.substring(2);
}
// Deaths
for(int i=0;i<32;i++) {
Deaths[i] = (int) RefreshMsg.charAt(1)+(int) RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(2);
}
// Pings
for(int i=0;i<32;i++) {
Ping[i] = (int) RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(1);
}
// ID's
for(int i=0;i<32;i++) {
Number[i] = (int) RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(1);
}
// IP's
for(int i=0;i<32;i++) {
IP[i] = new String();
for(int p=0;p<3;p++) {
IP[i] += (int) RefreshMsg.charAt(p)+".";
}
IP[i] += (int) RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(4);
}
// Team Scores
for(int i=0;i<4;i++) {
TeamScore[i] = (int) RefreshMsg.charAt(0)+(int) RefreshMsg.charAt(1);
RefreshMsg = RefreshMsg.substring(2);
}
// Map Name
tempNameLen = (int)RefreshMsg.charAt(0);
MapName = new String();
for(int p=1;p<=tempNameLen;p++) {
MapName += RefreshMsg.charAt(p);
}
RefreshMsg = RefreshMsg.substring(17);
// Times. Used and slightly modified from SARJ.
TimeLimit = ((int)RefreshMsg.charAt(3) << 24 |
(int)RefreshMsg.charAt(2) << 16 |
(int)RefreshMsg.charAt(1) << 8 |
(int)RefreshMsg.charAt(0))/3600;
RefreshMsg = RefreshMsg.substring(4);
CurrentTime = ((int)RefreshMsg.charAt(3) << 24 |
(int)RefreshMsg.charAt(2) << 16 |
(int)RefreshMsg.charAt(1) << 8 |
(int)RefreshMsg.charAt(0))/3600;
RefreshMsg = RefreshMsg.substring(4);
// Game score limit
GameScoreLimit = (int)RefreshMsg.charAt(0);
RefreshMsg = RefreshMsg.substring(1);
// Game Type
GameStyle = (int) RefreshMsg.charAt(1);
switch(GameStyle) {
case 0:
GameType = "Deathmatch";
break;
case 1:
GameType = "Pointmatch";
break;
case 2:
GameType = "Teammatch";
break;
case 3:
GameType = "CTF";
break;
case 4:
GameType = "Rambomatch";
break;
case 5:
GameType = "Infiltration";
break;
default:
GameType = "ERROR";
break;
}
}
}
// Sample Usage: "in" is a buffered reader
public void run() {
String line = new String();
while(Connected) {
try {
while(in.ready()) {
line += (char)in.read();
}
if(line.startsWith("REFRESH")) {
parseRefresh(line);
}
line = "";
} catch(IOException e) {
Connected = false;
}
}
}
« Last Edit: August 05, 2008, 12:19:55 am by iDante »

Offline mar77a

  • Global Moderator
  • Veteran
  • *****
  • Posts: 1295
  • mad
    • random stuffs
Re: REFRESH Command
« Reply #26 on: August 04, 2008, 12:16:02 pm »
Here's one for python, I basically copied it from ramirez's PHP :p

Code: [Select]
import struct
import string
import socket

# for code brevity
up = struct.unpack

long2ip = socket.inet_ntoa

def parseRefresh(sock):
    'Parses Soldat\'s REFRESH reply. Takes an open socket with the reply from REFRESH ready to be read.'

    info = { 'gamemode':0,
             'teammode':False,
             'pointmode':False,
             'players':0,
             'spectators':0,
             'map':'',
             'timelimit':0,
             'currenttime':0,
             'timeleft':0,
             'limit':0,
             'player':[],
             'spectator':[],
             'team':[]
             }

    # teams
    teams = {1:0, 2:0, 3:0, 4:0}
    # temp players
    players = []
    for i in range(0, 32):
        players.append({ 'name':'',
                       'ip':'',
                       'id':0,
                       'kills':0,
                       'deaths':0,
                       'team':0,
                       'ping':0 })

    # start reading stuff
   
    # player names
    for i in range(0, 32):
        data = sock.recv(25)
        l = up('B', data[0])[0]
        players[i]['name'] = data[1:l+1]

    # player teams
    for i in range(0, 32):
        data = sock.recv(1)
        players[i]['team'] = up('B', data)[0]

    # player kills
    for i in range(0, 32):
        data = sock.recv(2)
        players[i]['kills'] = up('H', data)[0]

    # player deaths
    for i in range(0, 32):
        data = sock.recv(2)
        players[i]['deaths'] = up('H', data)[0]

    # player pings
    for i in range(0, 32):
        data = sock.recv(1)
        players[i]['ping'] = up('B', data)[0]

    # player IDS
    for i in range(0, 32):
        data = sock.recv(1)
        players[i]['id'] = up('B', data)[0]

    # player IPs
    for i in range(0, 32):
        data = sock.recv(4)
        players[i]['ip'] = long2ip(data)

    # get team scores
    for i in range(1, 5):
        data = up('H', sock.recv(2))[0]
        teams[i] = data

    info['team'] = teams

    # get map name
    data = sock.recv(17)
    l = up('B', data[0])[0]
    info['map'] = data[1:l+1]

    # get time limit, current time and time left
    info['timelimit'] = up('L', sock.recv(4))[0]
    info['currenttime'] = up('L', sock.recv(4))[0]
    info['timeleft'] = info['timelimit'] - info['currenttime']

    # get limit
    info['limit'] = up('H', sock.recv(2))[0]

    #get gamemode
    gs = up('B', sock.recv(1))[0]
    info['gamemode'] = gs
    if gs == 2 or gs == 3 or gs == 5 or gs == 6:
        info['teammode'] = True
        if gs != 2:
            info['pointmode'] = True
    if gs != 2:
        if gs != 3 and gs != 5:
            del info['team'][1]
            del info['team'][2]
        del info['team'][3]
        del info['team'][4]

    in_players = []
    in_spectators = []
    for player in players:
        if player['team'] < 5:
            in_players.append(player)
        elif player['team'] == 5:
            in_spectators.append(player)

    info['player'] = in_players
    info['spectator'] = in_spectators
   
    # finall return it
    return info

######################
# example usage
######################

def getLine(sock):
    line = ''
    while 1:
        data = s.recv(1)
        line += data
        if data == '\n':
            break
    return line

# connect
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('lolcatshost', 23073))
# admin password
s.sendall('Fdfue34\r\n')
# request REFRESH
s.sendall('REFRESH\r\n')
# read and parse
refreshInfo = []
while 1:
    data = getLine(s)
    if data[:7] == 'REFRESH':
        refreshInfo = parseRefresh(s)
        break
print refreshInfo
# disconnect
s.close()
« Last Edit: August 04, 2008, 12:19:07 pm by mar77a »

Offline Flippeh

  • Major
  • *
  • Posts: 58
  • Fish go moo :>
    • My Homepage
Re: REFRESH Command
« Reply #27 on: July 23, 2009, 08:40:50 pm »
Bumpity bump!
Refresh parsing done the functional way, in Haskell.

Yeah, I was pretty bored today.

Code: [Select]
module Main where
{-
 - As with almost every code ever written in Haskell, this can surely be cut down to
 - less than half of it's current size, but for demonstration purpose I'll leave it
 - as is :)
 -
 - The Data.Binary module is not part of the current GHC distribution (GHC 6.10.3).
 - However, it's in the HackageDB on Haskell.org:
 - http://hackage.haskell.org/package/binary-0.5.0.1
 -}

import System.IO
import Control.Exception
import Control.Monad
import qualified Data.ByteString.Lazy.Char8 as B8
import Data.Binary.Get -- Sadly not part of the standart GHC distribution, see above
import Data.Word
import Data.List
import Network

-- Hardcoded login data
serverHost = "localhost"
serverPort = 23073
serverPass = "lol"

-- A team
data Team = None | Alpha | Bravo | Charlie | Delta | Spectator
          deriving (Show, Read, Eq, Ord)

-- Gamestyles
data GameStyle = Deathmatch | Pointmatch | Teammatch | CaptureTheFlag | Rambomatch  | Infiltration | HoldTheFlag
               deriving (Show, Read, Eq, Ord)

-- A single player
data Player = Player { nick   :: String,
                       kills  :: Int,
                       deaths :: Int,
                       team   :: Team,
                       id     :: Word8,
                       ping   :: Word8,
                       ip     :: [Word8] }
            deriving (Show, Read, Eq, Ord)

-- Everything combined into functional awesomeness
data Server = Server { currentMap  :: String,
                       gameStyle   :: GameStyle,
                       players     :: [Player],
                       teamScores  :: [Int],
                       killLimit   :: Int,
                       timeLimit   :: Int,
                       currentTime :: Int }
            deriving (Show, Read, Eq, Ord)
           
main :: IO ()
main = withSocketsDo $ do -- withSocketsDo initializes Winsock for Windows but is completly harmless for Unix
   state <- connect serverHost serverPort serverPass -- Using the Maybe type, did we succeed in connecting?
   case state of
      Just handle -> refresh handle >> disconnect handle -- Yes, refresh and disconnect
      Nothing     -> putStrLn $ "Could not connect to the server. Please check\n" ++ -- Bawww!
                                "your connection details and the password."

connect :: String -> Int -> String -> IO (Maybe Handle)
connect host port pass = do
   h <- try $ connectTo host (PortNumber $ fromIntegral port) :: IO (Either IOException Handle) -- try connecting
   case h of
      Left e       -> return Nothing -- Fail
      Right handle -> do
         hSetBuffering handle NoBuffering -- Disable buffering
         f <- hGetLine handle             -- Server greeting us?
         if f == "Soldat Admin Connection Established.\r" -- Yep.
            then do
               hPutStrLn handle pass -- Send the password
               result <- hGetLine handle -- check the result
               if result == "Welcome, you are in command of the server now.\r"
                  then do replicateM_ 2 (hGetLine handle) -- Yay, we'Re in. Get rid of those 2 lines telling us what to do
                          return $ Just handle -- return the handle into the IO Monad and stop evaluating
                  else return Nothing -- Fail again
            else return Nothing -- Triple Fail

disconnect :: Handle -> IO ()
disconnect handle = hClose handle -- Cut the wire

refresh :: Handle -> IO ()
refresh handle = do
   hPutStrLn handle "REFRESH" -- REFRESH!
   result <- hGetLine handle  -- Refresh?
   
   if result == "REFRESH\r"
      then do refreshData <- B8.hGet handle 1188 -- REFRESH! Get it!
              print $ runGet readServerInfo refreshData -- Parse it! Print it!
      else return () -- Fail :(

readServerInfo :: Get Server
readServerInfo = do
   names       <- replicateM 32 $ parseOneString 24 -- list of names
   teams       <- replicateM 32 getWord8            -- list of teams
   kills       <- replicateM 32 getWord16le         -- list of kills
   deaths      <- replicateM 32 getWord16le         -- list of deaths...
   pings       <- replicateM 32 getWord8            -- pings of lists
   ids         <- replicateM 32 getWord8            -- of ids list
   ips         <- replicateM 32 parseOneIP          -- IPS!
   teamscores  <- replicateM 4 getWord16le          -- team scores..
   mapname     <- parseOneString 16                 -- Zeh map
   timelimit   <- getWord32le                       -- time limit
   currenttime <- getWord32le                       -- time again, just not limit
   killlimit   <- getWord16le                       -- kills limit
   gamestyle   <- getWord8                          -- gamestyle!
   
   -- Because the name is the only reliable source that can not be 0 or empty for a valid player,
   -- we're using it to count how many players we have
   let numPlayers = length $ filter (not . B8.null) names
   -- Turn it into a list am BAM!
   let players = take numPlayers $ zipWith7 zipPlayers names teams kills deaths pings ids ips
   
   -- Return all that junk
   return Server { currentMap  = B8.unpack mapname,
                   gameStyle   = numToGamestyle gamestyle,
                   players     = players,
                   teamScores  = map fromIntegral teamscores,
                   killLimit   = fromIntegral killlimit,
                   timeLimit   = fromIntegral timelimit,
                   currentTime = fromIntegral currenttime }
                   
   -- I don't feel like commenting more :(
   -- Straight forward if you know a bit of Haskell.
 where zipPlayers n t k d p di pi =
          Player { nick    = B8.unpack n,
                   kills   = fromIntegral k,
                   deaths  = fromIntegral d,
                   team    = numToTeam t,
                   Main.id = di,
                   ping    = p,
                   ip      = pi }
                   
       parseOneString n = do
          len <- fmap fromIntegral getWord8
          str <- getLazyByteString n
          return $ B8.take len str
         
       parseOneIP = replicateM 4 getWord8
       
       numToGamestyle n = case n of
          0 -> Deathmatch
          1 -> Pointmatch
          2 -> Teammatch
          3 -> CaptureTheFlag
          4 -> Rambomatch
          5 -> Infiltration
          6 -> HoldTheFlag
         
       numToTeam n = case n of
          0 -> None
          1 -> Alpha
          2 -> Bravo
          3 -> Charlie
          4 -> Delta
          5 -> Spectator

Almost unreadable without syntax highlighting..
http://www.flippeh.de/Soldat.html
This one looks better!

EDIT: REFRESHX done.
Code: [Select]
import System.IO
import Control.Exception
import Control.Monad
import qualified Data.ByteString.Lazy.Char8 as B8
import Data.Binary.Get -- Sadly not part of the standart GHC distribution, see above
import Data.Word
import Data.List
import Control.Applicative
import Network
import Unsafe.Coerce

-- Hardcoded login data
serverHost = "localhost"
serverPort = 23073
serverPass = "lol"

-- A team
data Team = None | Alpha | Bravo | Charlie | Delta | Spectator
          deriving (Show, Read, Eq, Ord)

-- Gamestyles
data GameStyle = Deathmatch | Pointmatch | Teammatch | CaptureTheFlag | Rambomatch  | Infiltration | HoldTheFlag
               deriving (Show, Read, Eq, Ord)

-- A single player
data Player = Player { nick   :: String,
                       kills  :: Int,
                       caps   :: Int,
                       deaths :: Int,
                       team   :: Team,
                       userId :: Word8,
                       ping   :: Word32,
                       ip     :: [Word8],
                       coords :: (Float, Float) }
            deriving (Show, Read, Eq, Ord)

-- Everything combined into functional awesomeness
data Server = Server { currentMap  :: String,
                       nextMap     :: String,
                       gameStyle   :: GameStyle,
                       players     :: [Player],
                       maxPlayers  :: Int,
                       maxSpecs    :: Int,
                       teamScores  :: [Int],
                       killLimit   :: Int,
                       timeLimit   :: Int,
                       currentTime :: Int,
                       redFlagXY   :: (Float, Float),
                       bluFlagXY   :: (Float, Float),
                       hasPassword :: Int }
            deriving (Show, Read, Eq, Ord)
           
main :: IO ()
main = withSocketsDo $ do -- withSocketsDo initializes Winsock for Windows but is completly harmless for Unix
   state <- connect serverHost serverPort serverPass -- Using the Maybe type, did we succeed in connecting?
   case state of
      Just handle -> refresh handle >> disconnect handle -- Yes, refresh and disconnect
      Nothing     -> putStrLn $ "Could not connect to the server. Please check\n" ++ -- Bawww!
                                "your connection details and the password."

connect :: String -> Int -> String -> IO (Maybe Handle)
connect host port pass = do
   h <- try $ connectTo host (PortNumber $ fromIntegral port) :: IO (Either IOException Handle) -- try connecting
   case h of
      Left e       -> return Nothing -- Fail
      Right handle -> do
         hSetBuffering handle NoBuffering -- Disable buffering
         f <- hGetLine handle             -- Server greeting us?
         if f == "Soldat Admin Connection Established.\r" -- Yep.
            then do
               hPutStrLn handle pass -- Send the password
               result <- hGetLine handle -- check the result
               if result == "Welcome, you are in command of the server now.\r"
                  then do replicateM_ 2 (hGetLine handle) -- Yay, we'Re in. Get rid of those 2 lines telling us what to do
                          return $ Just handle -- return the handle into the IO Monad and stop evaluating
                  else return Nothing -- Fail again
            else return Nothing -- Triple Fail

disconnect :: Handle -> IO ()
disconnect handle = hClose handle -- Cut the wire

refresh :: Handle -> IO ()
refresh handle = do
   hPutStrLn handle "REFRESHX" -- REFRESH!
   result <- hGetLine handle  -- Refresh?
   
   if result == "REFRESHX\r"
      then do refreshData <- B8.hGet handle 1608 -- REFRESH! Get it!
              print $ runGet readServerInfo refreshData -- Parse it! Print it!
      else return () -- Fail :(

readServerInfo :: Get Server
readServerInfo = do
   names       <- replicateM 32 $ parseOneString 24 -- list of names
   teams       <- replicateM 32 getWord8            -- list of teams
   kills       <- replicateM 32 getWord16le         -- list of kills
   caps        <- replicateM 32 getWord8
   deaths      <- replicateM 32 getWord16le         -- list of deaths...
   pings       <- replicateM 32 getWord32le            -- pings of lists
   ids         <- replicateM 32 getWord8            -- of ids list
   ips         <- replicateM 32 parseOneIP          -- IPS!
   xs          <- replicateM 32 getWord32le
   ys          <- replicateM 32 getWord32le
   redXY       <- replicateM 2 getWord32le
   bluXY       <- replicateM 2 getWord32le
   teamscores  <- replicateM 4 getWord16le          -- team scores..
   mapname     <- parseOneString 16                 -- Zeh map
   timelimit   <- getWord32le                       -- time limit
   currenttime <- getWord32le                       -- time again, just not limit
   killlimit   <- getWord16le                       -- kills limit
   gamestyle   <- getWord8                          -- gamestyle!
   maxP        <- getWord8
   maxSpec     <- getWord8
   pass        <- getWord8                          -- BUG? This is always 0x01 (True).
                                                    -- Might be server related, as...
   nextmap     <- parseOneString 16                 -- ...this next map here reads very well, uncorrupted.

   -- Because the name is the only reliable source that can not be 0 or empty for a valid player,
   -- we're using it to count how many players we have
   let numPlayers = length $ filter (not . B8.null) names
   -- Turn it into a list am BAM!
   let players = take numPlayers $ getZipList $ zipPlayers
                    <$> ZipList names  -- Because the player informations
                    <*> ZipList teams  -- are split up all over the place
                    <*> ZipList kills  -- in about 10 different arrays,
                    <*> ZipList caps   -- we have to use ZipList.
                    <*> ZipList deaths -- zipWith7 worked well for REFRESH
                    <*> ZipList pings  -- But as for the case of REFRESHXH
                    <*> ZipList ids    -- we need more lists to zip!
                    <*> ZipList ips    -- This one actually scales much better
                    <*> ZipList xs     -- And looks kind of awesome, too!
                    <*> ZipList ys
   
   -- Return all that junk
   return Server { currentMap  = B8.unpack mapname,
                   nextMap     = B8.unpack nextmap,
                   gameStyle   = numToGamestyle gamestyle,
                   players     = players,
                   maxPlayers  = fromIntegral maxP,
                   maxSpecs    = fromIntegral maxSpec,
                   teamScores  = map fromIntegral teamscores,
                   killLimit   = fromIntegral killlimit,
                   timeLimit   = fromIntegral timelimit,
                   currentTime = fromIntegral currenttime,
                   redFlagXY   = (floatify $ redXY !! 0, floatify $ redXY !! 1),
                   bluFlagXY   = (floatify $ bluXY !! 0, floatify $ bluXY !! 1),
                   hasPassword = fromIntegral pass }
                   
   -- I don't feel like commenting more :(
   -- Straight forward if you know a bit of Haskell.
 where zipPlayers n t k c d p di pi x y =
          Player { nick    = B8.unpack n,
                   kills   = fromIntegral k,
                   caps    = fromIntegral c,
                   deaths  = fromIntegral d,
                   team    = numToTeam t,
                   userId  = di,
                   ping    = p,
                   ip      = pi,
                   coords  = (floatify x, floatify y) }
                   
       floatify n = unsafeCoerce n :: Float     
       
       parseOneString n = do
          len <- fmap fromIntegral getWord8
          str <- getLazyByteString n
          return $ B8.take len str
         
       parseOneIP = replicateM 4 getWord8
       
       numToGamestyle n = case n of
          0 -> Deathmatch
          1 -> Pointmatch
          2 -> Teammatch
          3 -> CaptureTheFlag
          4 -> Rambomatch
          5 -> Infiltration
          6 -> HoldTheFlag
         
       numToTeam n = case n of
          0 -> None
          1 -> Alpha
          2 -> Bravo
          3 -> Charlie
          4 -> Delta
          5 -> Spectator

No idea if anyone else is using Haskell here, but it sure is an awesome language.
« Last Edit: July 24, 2009, 08:06:03 am by Flippeh »