quoridor-hs-0.1.1.0: src/Quoridor/Cmdline/Network/Common.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Quoridor.Cmdline.Network.Common
( ConnPlayer (..)
, Message (..)
, sendMsg
, recvMsg
, isAliveSock
) where
import Control.Exception (Exception, SomeException, catch,
handle, throw)
import Control.Monad.State (MonadIO, liftIO, replicateM_)
import qualified Data.ByteString.Char8 as BC
import Data.Maybe (fromJust)
import Data.Typeable (Typeable)
import Text.Printf (printf)
import Control.Concurrent.STM (TChan)
import Network.Simple.TCP (Socket, recv, send)
import Numeric (readHex, showHex)
import Quoridor
data ConnPlayer = ConnPlayer
{ coplChan :: TChan Message
, coplColor :: Color
}
data NetworkException = NetworkException
deriving (Show, Typeable)
instance Exception NetworkException
data Message = FstGameMsg GameConfig Color
| GameMsg GameState ValidMoves String
| ChatMsg Color String
| TurnMsg Turn
| WaitMsg String
deriving (Read, Show)
-- | Type-safer than sendToSock/recvFromSock duo
sendMsg :: (MonadIO m) => Message -> Socket -> m ()
sendMsg = sendToSock
sendToSock :: (Show s, MonadIO m) => s -> Socket -> m ()
sendToSock s sock = do
send sock $ BC.pack $
printf "%04s" $ showHex (BC.length serialized) ""
send sock serialized
where serialized = BC.pack $ show s
isAliveSock :: (MonadIO m) => Socket -> m Bool
isAliveSock sock = liftIO $
do replicateM_ 2 $ (send sock . BC.pack) "0000"
return True
`catch`
\(_ :: SomeException) -> return False
-- | Type-safer than sendToSock/recvFromSock duo
recvMsg :: (MonadIO m, Functor m) => Socket -> m Message
recvMsg = recvFromSock
{-recvMsg = (fromJust <$>) . recvFromSock-}
recvFromSock :: (Read r, Functor m, MonadIO m) => Socket -> m r
recvFromSock sock = liftIO $ handle handler $ do
mHexSize <- recv sock 4
let ((size,_):_) = readHex $ BC.unpack $ fromJust mHexSize
if size == 0
-- To handle size 0 messages which are just isAlive check
then recvFromSock sock
else do
mValue <- recv sock size
return $ read $ BC.unpack $ fromJust mValue
where
handler (_ :: SomeException) = throw NetworkException