# HG changeset patch # User unc0rr # Date 1359750807 -14400 # Node ID f6abe50095d2b3684403be7786f18a759d6b2e7b # Parent da6b569ac930a5473e8b3366591b02abdba08edd Start work on the checker. Not it could connect to the server and... crash it. diff -r da6b569ac930 -r f6abe50095d2 gameServer/OfficialServer/checker.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gameServer/OfficialServer/checker.hs Sat Feb 02 00:33:27 2013 +0400 @@ -0,0 +1,114 @@ +{-# LANGUAGE CPP, ScopedTypeVariables, OverloadedStrings #-} +module Main where + +import qualified Control.Exception as Exception +import System.IO +import System.Log.Logger +import qualified Data.ConfigFile as CF +import Control.Monad.Error +import System.Directory +import Control.Monad.State +import Control.Concurrent.Chan +import Control.Concurrent +import Network +import Network.BSD +import Network.Socket hiding (recv) +import Network.Socket.ByteString +import qualified Data.ByteString.Char8 as B +#if !defined(mingw32_HOST_OS) +import System.Posix +#endif + +data Message = Packet [B.ByteString] + deriving Show + +protocolNumber = "43" + +takePacks :: State B.ByteString [[B.ByteString]] +takePacks = do + modify (until (not . B.isPrefixOf pDelim) (B.drop 2)) + packet <- state $ B.breakSubstring pDelim + buf <- get + if B.null buf then put packet >> return [] else + if B.null packet then return [] else do + packets <- takePacks + return (B.splitWith (== '\n') packet : packets) + where + pDelim = "\n\n" + + +recvLoop :: Socket -> Chan Message -> IO () +recvLoop s chan = + ((receiveWithBufferLoop B.empty >> return "Connection closed") + `Exception.catch` (\(e :: Exception.SomeException) -> return . B.pack . show $ e) + ) + >>= disconnected + where + disconnected msg = writeChan chan $ Packet ["BYE", msg] + receiveWithBufferLoop recvBuf = do + recvBS <- recv s 4096 + unless (B.null recvBS) $ do + let (packets, newrecvBuf) = runState takePacks $ B.append recvBuf recvBS + forM_ packets sendPacket + receiveWithBufferLoop $ B.copy newrecvBuf + + sendPacket packet = writeChan chan $ Packet packet + + +session :: B.ByteString -> B.ByteString -> Socket -> IO () +session l p s = do + noticeM "Core" "Connected" + coreChan <- newChan + forkIO $ recvLoop s coreChan + forever $ do + p <- readChan coreChan + case p of + Packet p -> do + debugM "Network" $ "Recv: " ++ show p + onPacket p + where + answer :: [B.ByteString] -> IO () + answer p = do + debugM "Network" $ "Send: " ++ show p + sendAll s $ B.unlines p `B.snoc` '\n' + onPacket :: [B.ByteString] -> IO () + onPacket ("CONNECTED":_) = answer ["CHECKER", protocolNumber, l, p] + onPacket ["PING"] = answer ["PONG"] + onPacket ("BYE" : xs) = error $ show xs + onPacket _ = return () + + +main :: IO () +main = withSocketsDo $ do +#if !defined(mingw32_HOST_OS) + installHandler sigPIPE Ignore Nothing; +#endif + + updateGlobalLogger "Core" (setLevel DEBUG) + updateGlobalLogger "Network" (setLevel DEBUG) + + Right (login, password) <- runErrorT $ do + d <- liftIO $ getHomeDirectory + conf <- join . liftIO . CF.readfile CF.emptyCP $ d ++ "/.hedgewars/hedgewars.ini" + l <- CF.get conf "net" "nick" + p <- CF.get conf "net" "passwordhash" + return (B.pack l, B.pack p) + + + Exception.bracket + setupConnection + (\s -> noticeM "Core" "Shutting down" >> sClose s) + (session login password) + where + setupConnection = do + noticeM "Core" "Connecting to the server..." + + proto <- getProtocolNumber "tcp" + let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] } + (addr:_) <- getAddrInfo (Just hints) (Just serverAddress) Nothing + let (SockAddrInet _ host) = addrAddress addr + sock <- socket AF_INET Stream proto + connect sock (SockAddrInet 46631 host) + return sock + + serverAddress = "netserver.hedgewars.org"