tremulous-query 1.0.3 → 1.0.4
raw patch · 11 files changed
+293/−234 lines, 11 files
Files
- Network/Tremulous/ByteStringUtils.hs +33/−11
- Network/Tremulous/MicroTime.hs +1/−1
- Network/Tremulous/NameInsensitive.hs +24/−23
- Network/Tremulous/Polling.hs +76/−87
- Network/Tremulous/Protocol.hs +56/−74
- Network/Tremulous/Scheduler.hs +27/−20
- Network/Tremulous/SocketExtensions.hs +16/−5
- Network/Tremulous/StrictMaybe.hs +43/−0
- Network/Tremulous/TupleReader.hs +9/−6
- Network/Tremulous/Util.hs +3/−3
- tremulous-query.cabal +5/−4
Network/Tremulous/ByteStringUtils.hs view
@@ -1,25 +1,47 @@ module Network.Tremulous.ByteStringUtils where-import Prelude as P-import Control.Applicative-import Control.DeepSeq-import Data.ByteString.Char8 as B+import qualified Prelude as P+import Prelude hiding (length, Maybe(..), null, dropWhile, drop)+import Data.ByteString.Char8 import Data.Char--instance NFData ByteString +import Network.Tremulous.StrictMaybe+import Data.ByteString.Internal+import Foreign stripPrefix :: ByteString -> ByteString -> Maybe ByteString stripPrefix p xs- | p `isPrefixOf` xs = Just $ B.drop (B.length p) xs- | otherwise = Nothing + | p `isPrefixOf` xs = Just $ drop (length p) xs+ | otherwise = Nothing maybeInt :: ByteString -> Maybe Int-maybeInt x = fst <$> readInt x+maybeInt x = case readInt x of+ P.Nothing -> Nothing+ P.Just (a, _) -> Just a splitlines :: ByteString -> [ByteString] splitlines = splitfilter '\n' stripw :: ByteString -> ByteString-stripw = B.dropWhile isSpace+stripw = dropWhile isSpace splitfilter :: Char -> ByteString -> [ByteString]-splitfilter f = P.filter (not . B.null) . split f+splitfilter f = P.filter (not . null) . split f+++rebuild :: Int -> (Word8 -> ByteString -> (Word8, ByteString)) -> ByteString -> ByteString+rebuild i f x0+ | i < 0 = empty+ | otherwise = unsafePerformIO $ createAndTrim i $ \p -> go p x0 0+ where+ go !p !(PS x s l) !n+ | l == 0 || n == i = return n+ | otherwise = do+ w <- withForeignPtr x (`peekByteOff` s)+ let ps' = PS x (s+1) (l-1)+ let (w', ps'') = f w ps'+ poke p w'+ go (p `plusPtr` 1) ps'' (n+1)++rebuildC :: Int -> (Char -> ByteString -> (Char, ByteString)) -> ByteString -> ByteString+rebuildC i f x0 = rebuild i f' x0+ where+ f' w ps = let (a, b) = f (w2c w) ps in (c2w a, b)
Network/Tremulous/MicroTime.hs view
@@ -16,7 +16,7 @@ #ifdef mingw32_HOST_OS getMicroTime = do FILETIME ft <- System.Win32.Time.getSystemTimeAsFileTime- return ((ft - win32_epoch_adjust) `div` 10)+ return ((ft - win32_epoch_adjust) `quot` 10) win32_epoch_adjust :: Word64 win32_epoch_adjust = 116444736000000000
Network/Tremulous/NameInsensitive.hs view
@@ -1,46 +1,47 @@ module Network.Tremulous.NameInsensitive (- TI(..), mk, mkColor, mkAlphaNum, unpackorig+ TI(..), mk, mkColor, mkAlphaNum ) where-import Control.DeepSeq-import Data.ByteString.Char8 as B+import Prelude hiding (length, map, filter)+import Data.ByteString.Char8 import Data.Char import Data.Ord+import Network.Tremulous.ByteStringUtils -data TI = TI { - original :: !ByteString- , cleanedCase :: !ByteString+data TI = TI {+ original :: !ByteString+ , cleanedCase :: !ByteString } --- Already strict-instance NFData TI- instance Eq TI where a == b = cleanedCase a == cleanedCase b- + instance Ord TI where compare = comparing cleanedCase- + instance Show TI where show = show . original -unpackorig :: TI -> String-unpackorig = B.unpack . original- mk :: ByteString -> TI-mk bs = TI bs (B.map toLower bs)+mk xs = TI bs (map toLower bs)+ where bs = clean xs mkColor :: ByteString -> TI-mkColor bs = TI bs (B.map toLower $ removeColors bs)+mkColor xs = TI bs (removeColors bs)+ where bs = clean xs mkAlphaNum :: ByteString -> TI-mkAlphaNum bs = TI bs (B.map toLower $ B.filter isAlphaNum bs)+mkAlphaNum xs = TI bs (map toLower $ filter (\x -> isAlphaNum x || isSpace x) bs)+ where bs = clean xs +clean :: ByteString -> ByteString+clean = stripw . filter (\c -> c >= '\x20' && c <= '\x7E')+ removeColors :: ByteString -> ByteString-removeColors = B.unfoldr f where- f bs | Just ('^', xs) <- t- , Just (x, xs2) <- B.uncons xs- , isAlphaNum x = B.uncons xs2- | otherwise = t- where t = B.uncons bs+removeColors bss = rebuildC (length bss) f bss where+ f '^' xs | Just (x2, xs2) <- uncons xs+ , isAlphaNum x2+ , Just (x3, xs3) <- uncons xs2+ = f x3 xs3+ f x xs = (toLower x, xs)
Network/Tremulous/Polling.hs view
@@ -1,22 +1,21 @@ module Network.Tremulous.Polling ( pollMasters, pollOne ) where-import Prelude hiding (all, concat, mapM_, elem, sequence_, concatMap, catch)+import Prelude hiding (all, concat, mapM_, elem, sequence_, concatMap, catch, Maybe(..), maybe, foldr)+import qualified Data.Maybe as P -import Control.DeepSeq-import Control.Monad hiding (mapM_, sequence_)+import Control.Monad (when) import Control.Concurrent import Control.Applicative import Control.Exception import Data.Foldable-import Data.Set (Set)-import qualified Data.Set as S import Data.Map (Map) import qualified Data.Map as M import Data.String import Data.ByteString.Char8 (ByteString, append, pack) +import Network.Tremulous.StrictMaybe import Network.Socket hiding (send, sendTo, recv, recvFrom) import Network.Socket.ByteString import Network.Tremulous.Protocol@@ -24,7 +23,8 @@ import Network.Tremulous.MicroTime import Network.Tremulous.Scheduler -data QType = QMaster !Int !Int | QGame !Int | QJustWait deriving Show+data QType = QMaster !Int !Int | QGame !Int | QJustWait+data State = Pending MasterServer | Requested !MicroTime MasterServer | Responded | Broken mtu :: Int mtu = 2048@@ -34,101 +34,105 @@ getServers :: Int -> ByteString getServers proto = "\xFF\xFF\xFF\xFFgetservers " `append` pack (show proto) `append` " empty full" + pollMasters :: Delay -> [MasterServer] -> IO PollResult pollMasters Delay{..} masterservers = do sock <- socket AF_INET Datagram defaultProtocol bindSocket sock (SockAddrInet 0 0)- + finished <- newEmptyMVar- - -- server addresses recieved by master- mstate <- newMVar S.empty- -- Servers that has responded- tstate <- newMVar S.empty- - -- When first packet was sent to server (removed on proper response)- pingstate <- newMVar (M.empty :: Map SockAddr MicroTime) + state <- newMVar (M.empty :: Map SockAddr State)+ let sf sched host qtype = case qtype of QGame n -> do now <- getMicroTime+ -- The first packet sent, which is why it's okey to just insert and replace+ -- the current Pending value when (n == packetDuplication) $- pureModifyMVar pingstate $ M.insert host now+ pureModifyMVar state $ M.update (\x -> case x of+ Pending ms -> P.Just (Requested now ms)+ _ -> P.Nothing)+ host sendTo sock getStatus host- if (n > 0) then do- addScheduled sched $ E (now + fromIntegral packetTimeout) host (QGame (n-1))- else do- addScheduled sched $ E (now + fromIntegral packetTimeout) host QJustWait- + addScheduled sched $ if (n > 0)+ then E (now + fromIntegral packetTimeout) host (QGame (n-1))+ else E (now + fromIntegral packetTimeout) host QJustWait+ QMaster n proto -> do now <- getMicroTime sendTo sock (getServers proto) host- if (n > 0) then do- addScheduled sched $ E (now + fromIntegral packetTimeout `div` 2) host (QMaster (n-1) proto)- else do- addScheduled sched $ E (now + fromIntegral packetTimeout) host QJustWait- + addScheduled sched $ if (n > 0)+ then E (now + fromIntegral packetTimeout `quot` 2) host (QMaster (n-1) proto)+ else E (now + fromIntegral packetTimeout) host QJustWait+ QJustWait -> return ()- + sched <- newScheduler throughputDelay sf (Just (putMVar finished () >> sClose sock)) addScheduledInstant sched $ map (\MasterServer{..} -> (masterAddress, QMaster (packetDuplication*4) masterProtocol)) masterservers let buildResponse = do packet <- forceIO finished $ recvFrom sock mtu- case parsePacket (map masterAddress masterservers) <$> packet of+ now <- getMicroTime+ case parsePacket <$> packet of -- The master responded, great! Now lets send requests to the new servers- Just (Master host x) -> do- deleteScheduled sched host- m <- takeMVar mstate- putMVar' mstate (S.union m x)- let delta = S.difference x m- when (S.size delta > 0) $- addScheduledInstant sched $ map (,QGame packetDuplication) (S.toList delta)- - buildResponse+ Just (Master host xs) -> case find (\x -> host == masterAddress x) masterservers of+ P.Just masterServer -> do+ deleteScheduled sched host+ s <- takeMVar state+ let (s', delta) = foldr (masterRoll masterServer) (s, []) xs+ addScheduledInstant sched $ map (,QGame packetDuplication) delta+ putMVar' state s'+ buildResponse + P.Nothing -> buildResponse+ Just (Tremulous host x) -> do- now <- getMicroTime- t <- takeMVar tstate- if S.member host t then do- putMVar' tstate t- buildResponse- else do- deleteScheduled sched host- ps <- takeMVar pingstate- start <- return $! M.lookup host ps- putMVar' pingstate $ M.delete host ps- putMVar' tstate $ S.insert host t- -- This also serves as protection against- -- receiving responses for requests never sent- case start of- Nothing -> buildResponse- Just a -> do- let gameping = fromIntegral (now - a) `div` 1000- ( strict x{ gameping } : ) `liftM` buildResponse + s <- takeMVar state+ case M.lookup host s of+ P.Just (Requested start MasterServer{..}) -> do+ deleteScheduled sched host+ -- This one is for you, devhc+ if protocol x == masterProtocol then do+ let gameping = fromIntegral (now - start) `quot` 1000+ putMVar' state $ M.insert host Responded s+ (x{ gameping } :) <$> buildResponse+ else do+ putMVar' state $ M.insert host Broken s+ buildResponse+ _ -> do+ putMVar' state s+ buildResponse Just Invalid -> buildResponse- + Nothing -> return [] xs <- buildResponse- m <- takeMVar mstate- t <- takeMVar tstate- return $! PollResult xs (S.size t) (S.size m) t- - where forceIO m f = catch (Just <$> f) $ \(_ :: IOError) -> do- b <- isEmptyMVar m + s <- takeMVar state+ let (nResp, nNot) = ssum s+ return (PollResult xs nResp (nResp+nNot))++ where+ forceIO m f = catch (Just <$> f) $ \(_ :: IOError) -> do+ b <- isEmptyMVar m if b then forceIO m f else return Nothing+ masterRoll ms host ~(!m, xs) | M.member host m = (m, xs)+ | otherwise = (M.insert host (Pending ms) m, host:xs)+ ssum = foldl' f (0, 0) where+ f (!a, !b) Responded = (a+1, b)+ f (!a, !b) _ = (a, b+1) -data Packet = Master !SockAddr !(Set SockAddr) | Tremulous !SockAddr !GameServer | Invalid -parsePacket :: [SockAddr] -> (ByteString, SockAddr) -> Packet-parsePacket masters (content, host) = case B.stripPrefix "\xFF\xFF\xFF\xFF" content of- Just a | Just x <- parseServer a -> Tremulous host x- | Just x <- parseMaster a, host `elem` masters -> Master host x- _ -> Invalid+data Packet = Master !SockAddr ![SockAddr] | Tremulous !SockAddr !GameServer | Invalid++parsePacket :: (ByteString, SockAddr) -> Packet+parsePacket (content, host) = case B.stripPrefix "\xFF\xFF\xFF\xFF" content of+ Just a | Just x <- parseServer a -> Tremulous host x+ | Just x <- parseMaster a -> Master host x+ _ -> Invalid where- parseMaster x = S.fromList . parseMasterServer <$> stripPrefix "getserversResponse" x+ parseMaster x = parseMasterServer <$> stripPrefix "getserversResponse" x parseServer x = parseGameServer host =<< stripPrefix "statusResponse" x @@ -143,12 +147,12 @@ else do sClose sock return Nothing- + start <- getMicroTime poll <- ioMaybe $ recv sock mtu <* killThread pid stop <- getMicroTime- let gameping = fromIntegral (stop - start) `div` 1000- return $ (\x -> x {gameping}) <$> + let gameping = fromIntegral (stop - start) `quot` 1000+ return $ (\x -> x {gameping}) <$> (parseGameServer sockaddr =<< isProper =<< poll) where err (_ :: IOError) = return Nothing@@ -156,18 +160,3 @@ ioMaybe :: IO a -> IO (Maybe a) ioMaybe f = catch (Just <$> f) (\(_ :: IOError) -> return Nothing)--putMVar' :: MVar a -> a -> IO ()-putMVar' m a = a `seq` putMVar m a--pureModifyMVar :: MVar a -> (a -> a) -> IO ()-pureModifyMVar m f = putMVar' m . f =<< takeMVar m- -strict :: NFData a => a -> a-strict x = x `deepseq` x--whileJust :: Monad m => a -> (a -> m (Maybe a)) -> m ()-whileJust x f = f x >>= \c -> case c of- Just a -> whileJust a f- Nothing -> return ()-
Network/Tremulous/Protocol.hs view
@@ -2,26 +2,21 @@ module Network.Tremulous.NameInsensitive , Delay(..), Team(..), GameServer(..), Player(..), MasterServer(..), PollResult(..) , defaultDelay, parseGameServer, proto2string, string2proto, parseMasterServer- , B.unpack ) where-import Prelude as P-import Control.Applicative-import Control.DeepSeq+import Prelude as P hiding (Maybe(..), maybe)+import Control.Applicative hiding (many) import Control.Monad.State.Strict -import Data.Attoparsec.Char8 as A hiding (option)+import Data.Attoparsec.Char8 hiding (option) import Data.Attoparsec (anyWord8) import Data.ByteString.Char8 as B-import Data.Maybe+import Network.Tremulous.StrictMaybe import Data.String-import Data.Char import Data.Bits import Data.Word-import Data.Set (Set)- import Network.Socket import Network.Tremulous.ByteStringUtils as B-import Network.Tremulous.SocketExtensions ()+import Network.Tremulous.SocketExtensions import Network.Tremulous.NameInsensitive import Network.Tremulous.TupleReader @@ -29,26 +24,28 @@ packetTimeout , packetDuplication , throughputDelay :: !Int- } deriving (Show, Read)+ } data MasterServer = MasterServer { masterAddress :: !SockAddr , masterProtocol :: !Int } deriving Eq- + data GameServer = GameServer { address :: !SockAddr- , gameping :: !Int+ , gameping , protocol :: !Int- , gamemod :: !(Maybe TI) , hostname :: !TI- , mapname :: !TI- , slots- , privslots :: !Int- , protected :: !Bool+ , gamemod+ , version+ , mapname :: !(Maybe TI)+ , slots :: !Int+ , privslots :: !(Maybe Int)+ , protected+ , unlagged :: !Bool , timelimit , suddendeath :: !(Maybe Int)- , unlagged :: !Bool+ , nplayers :: !Int , players :: ![Player] }@@ -66,30 +63,15 @@ polled :: ![GameServer] , serversResponded , serversRequested :: !Int- , respondedCache :: !(Set SockAddr) } --instance NFData MasterServer where- rnf (MasterServer a b) = rnf a `seq` rnf b--instance NFData Team--instance NFData GameServer where- rnf (GameServer a b c d e f g h i j k l m n) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d- `seq` rnf e `seq` rnf f `seq` rnf g `seq` rnf h `seq` rnf i `seq` rnf j - `seq` rnf k `seq` rnf l `seq` rnf m `seq` rnf n--instance NFData Player where- rnf (Player a b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d- defaultDelay :: Delay defaultDelay = Delay { packetTimeout = 400 * 1000 , packetDuplication = 2 , throughputDelay = 1 * 1000 }- + -- Protocol version proto2string :: IsString s => Int -> s proto2string x = case x of@@ -111,27 +93,28 @@ skipSpace ping <- signed decimal skipSpace- name <- mkColor . clean <$> quoted+ name <- mkColor <$> quoted return Player {..}- + -- cvar P parseP :: ByteString -> [Team]-parseP = foldr' f [] +parseP = foldr' f [] where f '-' xs = xs f a xs = readTeam a : xs- + readTeam x = case x of '0' -> Spectators '1' -> Aliens '2' -> Humans _ -> Unknown -parsePlayers :: ByteString -> [ByteString] -> Maybe [Player]-parsePlayers p = zipWithM parsePlayer (parseP p ++ repeat Unknown)+parsePlayers :: (Maybe ByteString) -> [ByteString] -> Maybe [Player]+parsePlayers Nothing xs = mapM (parsePlayer Unknown) xs+parsePlayers (Just p) xs = zipWithM parsePlayer (parseP p ++ repeat Unknown) xs parseCVars :: ByteString -> [(ByteString, ByteString)]-parseCVars xs = f (splitfilter '\\' xs) where +parseCVars xs = f (splitfilter '\\' xs) where f (k:v:cs) = (k, v) : f cs f _ = [] @@ -142,45 +125,47 @@ mkGameServer :: SockAddr -> [ByteString] -> [(ByteString, ByteString)] -> Maybe GameServer mkGameServer address rawplayers = tupleReader $ do- timelimit <- option Nothing maybeInt "timelimit"- hostname <- mkColor . clean <$> require "sv_hostname"- protocol <- requireWith maybeInt "protocol"- mapname <- option (TI "" "") (mk . clean) "mapname"- gamemod <- option Nothing mkMod "gamename"- p <- option "" id "P"+ timelimit <- optionWith maybeInt "timelimit"+ hostname <- mkColor <$> require "sv_hostname"+ protocol <- requireWith maybeInt "protocol"+ mapname <- optionWith (Just . mk) "mapname"+ version <- optionWith (Just . mk) "version"+ gamemod <- optionWith mkMod "gamename"+ p <- option "P" players <- lift $ parsePlayers p rawplayers- protected <- option False (/="0") "g_needpass"- privslots <- option 0 (fromMaybe 0 . maybeInt) "sv_privateClients"- slots <- requireWith maybeInt "sv_maxclients"- suddendeath <- option Nothing maybeInt "g_suddenDeathTime"- unlagged <- option False (/="0") "g_unlagged"- + protected <- maybe False (/="0") <$> option "g_needpass"+ privslots <- optionWith maybeInt "sv_privateClients"+ slots <- maybe id subtract privslots <$> requireWith maybeInt "sv_maxclients"+ suddendeath <- optionWith maybeInt "g_suddenDeathTime"+ unlagged <- maybe False (/="0") <$> option "g_unlagged"+ return GameServer { gameping = -1, nplayers = P.length players, .. } where mkMod "base" = Nothing- mkMod a = Just (mk (clean a))+ mkMod a = Just (mk a) -parseMasterServer :: ByteString -> [SockAddr] -parseMasterServer = fromMaybe [] . parseMaybe attoIP+parseMasterServer :: ByteString -> [SockAddr]+parseMasterServer = fromMaybe [] . parseMaybe (many addr) where- attoIP = A.many (char '\\' *> addr)- (.<<.) :: Bits a => a -> Int -> a- (.<<.) = shiftL - wg :: Integral i => Parser i- wg = fromIntegral <$> anyWord8 + wg = anyWord8+ f :: (Integral a, Integral b) => a -> b+ f = fromIntegral addr = do- -- try $ string "EOT\0\0\0" *> endOfInput- i0 <- wg- i1 <- wg- i2 <- wg+ char '\\' i3 <- wg- p0 <- wg+ i2 <- wg+ i1 <- wg+ i0 <- wg p1 <- wg- let ip = (i3 .<<. 24) .|. (i2 .<<. 16) .|. (i1 .<<. 8) .|. i0 :: Word32- let port = (p0 .<<. 8) .|. p1 :: Word16- return $ SockAddrInet (fromIntegral port) ip-+ p0 <- wg+ let ip = (f i3 .<<. 24) .|. (f i2 .<<. 16) .|. (f i1 .<<. 8) .|. f i0 :: Word32+ port = (f p1 .<<. 8) .|. f p0 :: Word16+ if port == 0 || ip == 0+ then addr+ else return $ SockAddrInet (PortNum (htons port)) (htonl ip)+(.<<.) :: Bits a => a -> Int -> a+(.<<.) = shiftL -- /// Attoparsec utils //////////////////////////////////////////////////////////////////////////// quoted :: Parser ByteString@@ -190,6 +175,3 @@ parseMaybe f xs = case parseOnly f xs of Right a -> Just a Left _ -> Nothing--clean :: ByteString -> ByteString-clean = stripw . B.filter isPrint
Network/Tremulous/Scheduler.hs view
@@ -2,7 +2,10 @@ Event(..), Scheduler , newScheduler, addScheduled, addScheduledBatch , addScheduledInstant, deleteScheduled+ , putMVar', pureModifyMVar ) where+import Prelude hiding (Maybe(..))+import Network.Tremulous.StrictMaybe import Control.Monad import Control.Concurrent import Control.Exception@@ -12,13 +15,13 @@ data Interrupt = Interrupt deriving (Typeable, Show)- + instance Exception Interrupt -data (Eq id, Ord id) => Scheduler id a = Scheduler- { sync :: !(MVar ())- , queue :: !(MVar [Event id a])- }+data Scheduler id a = (Eq id, Ord id) =>+ Scheduler { sync :: !(MVar ())+ , queue :: !(MVar [Event id a])+ } data Event id a = E { time :: !MicroTime@@ -27,13 +30,13 @@ } newScheduler :: (Eq a, Ord a) => Int -> (Scheduler a b -> a -> b -> IO ()) -> Maybe (IO ()) -> IO (Scheduler a b)-newScheduler throughput func finalizer = do +newScheduler throughput func finalizer = do queue <- newMVar [] sync <- newEmptyMVar let sched = Scheduler{..} uninterruptibleMask $ \a -> forkIO $ runner sched a return sched- + where runner sched@Scheduler{..} restore = do takeMVar sync@@ -41,28 +44,28 @@ let loop = do q <- takeMVar queue case q of- [] -> putMVar queue q >> case finalizer of+ [] -> putMVar' queue q >> case finalizer of Nothing -> do takeMVar sync loop Just a -> a- + E{time=0, ..} : qs -> do- putMVar queue qs+ putMVar' queue qs func sched idn storage when (throughput > 0) (threadDelay throughput) loop- + E {..} : qs -> do now <- getMicroTime if now >= time then do- putMVar queue qs+ putMVar' queue qs func sched idn storage when (throughput > 0) (threadDelay throughput) else do- putMVar queue q+ putMVar' queue q tryTakeMVar sync syncid <- forkIOUnmasked $ takeMVar sync >> throwTo tid Interrupt let wait = fromIntegral (time - now)@@ -79,22 +82,22 @@ signal :: MVar () -> IO () signal a = tryPutMVar a () >> return () -addScheduled :: (Ord id, Eq id) => Scheduler id a -> Event id a -> IO ()+addScheduled :: Scheduler id a -> Event id a -> IO () addScheduled Scheduler{..} event = do pureModifyMVar queue $ insertTimed event signal sync- -addScheduledBatch :: (Ord id, Eq id, Foldable f) => Scheduler id a -> f (Event id a) -> IO ()++addScheduledBatch :: Foldable f => Scheduler id a -> f (Event id a) -> IO () addScheduledBatch Scheduler{..} events = do pureModifyMVar queue $ \q -> foldl' (flip insertTimed) q events signal sync -addScheduledInstant :: (Ord id, Eq id) => Scheduler id a -> [(id, a)] -> IO ()+addScheduledInstant :: Scheduler id a -> [(id, a)] -> IO () addScheduledInstant Scheduler{..} events = do pureModifyMVar queue $ \q -> (map (uncurry (E 0)) events) ++ q signal sync -deleteScheduled :: (Ord id, Eq id) => Scheduler id a -> id -> IO ()+deleteScheduled :: Scheduler id a -> id -> IO () deleteScheduled Scheduler{..} ident = do pureModifyMVar queue $ deleteID ident signal sync@@ -115,5 +118,9 @@ | otherwise -> x : deleteID match xs [] -> [] -pureModifyMVar ::MVar a -> (a -> a) -> IO ()-pureModifyMVar m f = putMVar m . f =<< takeMVar m+putMVar' :: MVar a -> a -> IO ()+putMVar' m !a = putMVar m a++pureModifyMVar :: MVar a -> (a -> a) -> IO ()+pureModifyMVar m f = putMVar' m . f =<< takeMVar m+
Network/Tremulous/SocketExtensions.hs view
@@ -1,15 +1,26 @@ {-# LANGUAGE CPP, StandaloneDeriving #-} module Network.Tremulous.SocketExtensions where+import Prelude as P+import Foreign import Control.DeepSeq import Network.Socket deriving instance Ord SockAddr instance NFData SockAddr where- rnf (SockAddrInet (PortNum p) h) = rnf p `seq` rnf h- rnf (SockAddrInet6 (PortNum p) f h s) = rnf p `seq` rnf f `seq` rnf h `seq` rnf s-#ifdef linux_HOST_OS- rnf (SockAddrUnix s) = rnf s+ rnf (SockAddrInet (PortNum a) b) = rnf a `seq` rnf b+ rnf (SockAddrInet6 (PortNum a) b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d+#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)+ rnf (SockAddrUnix a) = rnf a #endif- +#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+#define CALLCONV stdcall+#else+#define CALLCONV ccall+#endif++foreign import CALLCONV unsafe "ntohl" ntohl :: Word32 -> Word32+foreign import CALLCONV unsafe "htonl" htonl :: Word32 -> Word32+foreign import CALLCONV unsafe "ntohs" ntohs :: Word16 -> Word16+foreign import CALLCONV unsafe "htons" htons :: Word16 -> Word16
+ Network/Tremulous/StrictMaybe.hs view
@@ -0,0 +1,43 @@+{- Drop in replacement for Data.Maybe+-- It was easier to just use this than messing around with seq+-}+module Network.Tremulous.StrictMaybe where+import Prelude hiding(Maybe(..), maybe)++data Maybe a = Nothing | Just !a+ deriving (Eq, Ord, Show, Read)++instance Functor Maybe where+ fmap _ Nothing = Nothing+ fmap f (Just x) = Just (f x)++instance Monad Maybe where+ Nothing >>= _ = Nothing+ Just a >>= f = f a+ return = Just+ fail _ = Nothing++isJust :: Maybe a -> Bool+isJust Nothing = False+isJust _ = True++isNothing :: Maybe a -> Bool+isNothing Nothing = True+isNothing _ = False++fromMaybe :: a -> Maybe a -> a+fromMaybe x Nothing = x+fromMaybe _ (Just y) = y++maybe :: b -> (a -> b) -> Maybe a -> b+maybe x _ Nothing = x+maybe _ f (Just y) = f y++whenJust :: Monad m => Maybe t -> (t -> m ())-> m ()+whenJust (Just a) f = f a+whenJust Nothing _ = return ()++whileJust :: Monad m => a -> (a -> m (Maybe a)) -> m ()+whileJust x f = f x >>= \c -> case c of+ Just a -> whileJust a f+ Nothing -> return ()
Network/Tremulous/TupleReader.hs view
@@ -1,7 +1,7 @@-module Network.Tremulous.TupleReader (- TupleReader, tupleReader, require, requireWith, option-) where+module Network.Tremulous.TupleReader where import Control.Monad.State.Strict+import Network.Tremulous.StrictMaybe+import Prelude (Eq(..), otherwise, ($)) type TupleReader k v a = StateT [(k, v)] Maybe a @@ -23,10 +23,13 @@ e <- with key lift $ f =<< e -option :: Eq k => a -> (v -> a) -> k -> TupleReader k v a-option def f key = maybe def f `fmap` with key+optionWith :: Eq k => (v -> Maybe a) -> k -> TupleReader k v (Maybe a)+optionWith f key = do+ e <- with key+ return (f =<< e) -with :: Eq k => k -> TupleReader k v (Maybe v)+with, option :: Eq k => k -> TupleReader k v (Maybe v)+option = with with key = do s <- get let (e, s') = lookupDelete key s
Network/Tremulous/Util.hs view
@@ -24,11 +24,11 @@ elemByAddress add = any (\x -> add == address x) search :: String -> [GameServer] -> [(TI, GameServer)]-search "" = makePlayerNameList -search rawstr = filter (\(a, _) -> str `B.isInfixOf` cleanedCase a ) . makePlayerNameList +search "" = makePlayerNameList+search rawstr = filter (\(a, _) -> str `B.isInfixOf` cleanedCase a ) . makePlayerNameList where str = B.pack $ map toLower rawstr- + makePlayerNameList :: [GameServer] -> [(TI, GameServer)] makePlayerNameList = concatMap $ \x -> map (\a -> (name a, x)) (players x)
tremulous-query.cabal view
@@ -1,5 +1,5 @@ Name: tremulous-query-Version: 1.0.3+Version: 1.0.4 Author: Christoffer Öjeling Maintainer: Christoffer Öjeling <christoffer@ojeling.net> License: GPL-3@@ -19,7 +19,7 @@ Library Default-Language: Haskell2010- Default-Extensions: BangPatterns TupleSections NamedFieldPuns RecordWildCards+ Default-Extensions: BangPatterns TupleSections NamedFieldPuns RecordWildCards GADTs ScopedTypeVariables OverloadedStrings DeriveDataTypeable Other-Extensions: CPP StandaloneDeriving Exposed-Modules: Network.Tremulous.Protocol@@ -30,9 +30,10 @@ Network.Tremulous.ByteStringUtils Network.Tremulous.Scheduler Network.Tremulous.MicroTime- Other-Modules: Network.Tremulous.TupleReader+ Network.Tremulous.StrictMaybe+ Network.Tremulous.TupleReader - Build-Depends: base>=4.3 && < 5, network, deepseq, containers, bytestring, attoparsec, mtl+ Build-Depends: base>=4.3 && < 5, network, containers, bytestring, attoparsec, mtl, deepseq if os(windows) Build-Depends: Win32 Ghc-Options: -Wall -fno-warn-unused-do-bind -fno-warn-orphans -funbox-strict-fields