netserver/newhwserv.hs
author unc0rr
Wed, 30 Apr 2008 20:14:09 +0000
changeset 891 701f86df9b4c
parent 890 1d8c4a5ec622
child 892 dfe97199f17e
permissions -rw-r--r--
Properly handle QUIT command. Now, we can concentrate on protocol implementation

module Main where

import Network
import IO
import System.IO
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception (finally)
import Control.Monad (forM, forM_, filterM, liftM)
import Data.List
import Miscutils
import HWProto

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 "" "" False)
	acceptLoop servSock acceptChan

listenLoop :: Handle -> TChan String -> IO ()
listenLoop handle chan = do
	str <- hGetLine handle
	atomically $ writeTChan chan str
	listenLoop handle chan

clientLoop :: Handle -> TChan String -> IO ()
clientLoop handle chan =
	listenLoop handle chan
		`catch` (const $ clientOff >> return ())
		`finally` hClose handle
	where clientOff = atomically $ writeTChan chan "QUIT"

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 (ci:clients) rooms
		Right (line, client) -> do
			let (doQuit, recipients, strs) = handleCmd client sameRoom rooms $ words line

			clients' <- forM recipients $
					\ci -> do
							forM_ strs (\str -> hPutStrLn (handle ci) str)
							hFlush (handle ci)
							return []
					`catch` const (hClose (handle ci) >> return [ci])

			client' <- if doQuit then hClose (handle client) >> return [client] else return []

			mainLoop servSock acceptChan (remove (remove clients (concat clients')) client') rooms
			where
				sameRoom = filter (\cl -> room cl == room client) clients
				remove list rmClients = deleteFirstsBy (\ a b -> handle a == handle b) list rmClients

startServer serverSocket = do
	acceptChan <- atomically newTChan
	forkIO $ acceptLoop serverSocket acceptChan
	mainLoop serverSocket acceptChan [] []

main = withSocketsDo $ do
	serverSocket <- listenOn $ Service "hedgewars"
	startServer serverSocket `finally` sClose serverSocket