0 Members and 2 Guests are viewing this topic.
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; }}
REFRESHEx r = REFRESHEx.Get(ConnectedSocket);
    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;    }
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
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 readerpublic 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; } }}
import structimport stringimport socket# for code brevityup = struct.unpacklong2ip = socket.inet_ntoadef 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# connects = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect(('lolcatshost', 23073))# admin passwords.sendall('Fdfue34\r\n')# request REFRESHs.sendall('REFRESH\r\n')# read and parserefreshInfo = []while 1:  data = getLine(s)  if data[:7] == 'REFRESH':    refreshInfo = parseRefresh(s)    breakprint refreshInfo# disconnects.close()
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.IOimport Control.Exceptionimport Control.Monadimport qualified Data.ByteString.Lazy.Char8 as B8import Data.Binary.Get -- Sadly not part of the standart GHC distribution, see aboveimport Data.Wordimport Data.Listimport Network-- Hardcoded login dataserverHost = "localhost"serverPort = 23073serverPass = "lol"-- A teamdata Team = None | Alpha | Bravo | Charlie | Delta | Spectator deriving (Show, Read, Eq, Ord)-- Gamestylesdata GameStyle = Deathmatch | Pointmatch | Teammatch | CaptureTheFlag | Rambomatch | Infiltration | HoldTheFlag deriving (Show, Read, Eq, Ord)-- A single playerdata 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 awesomenessdata 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 Faildisconnect :: Handle -> IO ()disconnect handle = hClose handle -- Cut the wirerefresh :: 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 ServerreadServerInfo = 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
import System.IOimport Control.Exceptionimport Control.Monadimport qualified Data.ByteString.Lazy.Char8 as B8import Data.Binary.Get -- Sadly not part of the standart GHC distribution, see aboveimport Data.Wordimport Data.Listimport Control.Applicativeimport Networkimport Unsafe.Coerce-- Hardcoded login dataserverHost = "localhost"serverPort = 23073serverPass = "lol"-- A teamdata Team = None | Alpha | Bravo | Charlie | Delta | Spectator deriving (Show, Read, Eq, Ord)-- Gamestylesdata GameStyle = Deathmatch | Pointmatch | Teammatch | CaptureTheFlag | Rambomatch | Infiltration | HoldTheFlag deriving (Show, Read, Eq, Ord)-- A single playerdata 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 awesomenessdata 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 Faildisconnect :: Handle -> IO ()disconnect handle = hClose handle -- Cut the wirerefresh :: 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 ServerreadServerInfo = 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