diff -r f5b1b3fd70cc -r ff8863ebde17 netserver/hedgewars-server.hs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netserver/hedgewars-server.hs Sat Oct 18 11:57:46 2008 +0000 @@ -0,0 +1,94 @@ +module Main where + +import Network +import IO +import System.IO +import Control.Concurrent +import Control.Concurrent.STM +import Control.Exception (setUncaughtExceptionHandler, handle, finally) +import Control.Monad (forM, forM_, filterM, liftM) +import Maybe (fromMaybe) +import Data.List +import Miscutils +import HWProto +import Opts + +acceptLoop :: Socket -> TChan ClientInfo -> IO () +acceptLoop servSock acceptChan = do + (cHandle, host, port) <- accept servSock + cChan <- atomically newTChan + forkIO $ clientLoop cHandle cChan + atomically $ writeTChan acceptChan (ClientInfo cChan cHandle "" 0 "" False) + hPutStrLn cHandle "CONNECTED\n" + hFlush cHandle + acceptLoop servSock acceptChan + + +listenLoop :: Handle -> [String] -> TChan [String] -> IO () +listenLoop handle buf chan = do + str <- hGetLine handle + if str == "" then do + atomically $ writeTChan chan buf + listenLoop handle [] chan + else + listenLoop handle (buf ++ [str]) chan + + +clientLoop :: Handle -> TChan [String] -> IO () +clientLoop handle chan = + listenLoop handle [] chan + `catch` (const $ clientOff >> return ()) + where clientOff = atomically $ writeTChan chan ["QUIT"] -- if the client disconnects, we perform as if it sent QUIT message + + +sendAnswers [] _ clients _ = return clients +sendAnswers ((handlesFunc, answer):answers) client clients rooms = do + let recipients = handlesFunc client clients rooms + putStrLn ("< " ++ (show answer)) + + clHandles' <- forM recipients $ + \ch -> Control.Exception.handle (\e -> putStrLn (show e) >> hClose ch >> return [ch]) $ + if (not $ null answer) && (head answer == "off") then hClose ch >> return [ch] else -- probably client with exception, don't send him anything + do + forM_ answer (\str -> hPutStrLn ch str) + hPutStrLn ch "" + hFlush ch + if (not $ null answer) && (head answer == "BYE") then hClose ch >> return [ch] else return [] + + let mclients = remove clients $ concat clHandles' + + sendAnswers answers client mclients rooms + where + remove list rmClHandles = deleteFirstsBy2t (\ a b -> (Miscutils.handle a) == b) list rmClHandles + + +mainLoop :: Socket -> TChan ClientInfo -> [ClientInfo] -> [RoomInfo] -> IO () +mainLoop servSock acceptChan clients rooms = do + r <- atomically $ (Left `fmap` readTChan acceptChan) `orElse` (Right `fmap` tselect clients) + case r of + Left ci -> do + mainLoop servSock acceptChan (clients ++ [ci]) rooms + Right (cmd, client) -> do + putStrLn ("> " ++ show cmd) + + let (clientsFunc, roomsFunc, answers) = handleCmd client clients rooms $ cmd + let mrooms = roomsFunc rooms + let mclients = (clientsFunc clients) + let mclient = fromMaybe client $ find (== client) mclients + + clientsIn <- sendAnswers answers mclient mclients mrooms + + mainLoop servSock acceptChan clientsIn mrooms + + +startServer serverSocket = do + acceptChan <- atomically newTChan + forkIO $ acceptLoop serverSocket acceptChan + mainLoop serverSocket acceptChan [] [] + + +main = withSocketsDo $ do + flags <- opts + putStrLn $ "Listening on port " ++ show (getPort flags) + serverSocket <- listenOn $ PortNumber (getPort flags) + startServer serverSocket `finally` sClose serverSocket