gameServer/OfficialServer/checker.hs
changeset 8474 f6abe50095d2
child 8479 8d71109b04d2
--- /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"