2023-12-10 01:02:09 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2024-05-01 21:52:49 +00:00
|
|
|
module Server.Communication
|
|
|
|
( module Server.Communication
|
|
|
|
, module S
|
|
|
|
) where
|
2023-12-09 12:58:59 +00:00
|
|
|
|
2023-12-10 05:57:48 +00:00
|
|
|
import Control.Concurrent
|
|
|
|
|
|
|
|
import qualified Control.Concurrent.STM as STM
|
|
|
|
|
2024-05-01 21:53:12 +00:00
|
|
|
import Control.Exception
|
2024-03-29 09:34:15 +00:00
|
|
|
|
2023-12-09 12:58:59 +00:00
|
|
|
import Control.Monad
|
|
|
|
|
2023-12-10 01:02:09 +00:00
|
|
|
import Control.Monad.IO.Class
|
|
|
|
|
|
|
|
import Control.Monad.RWS.Strict
|
|
|
|
|
2024-11-03 03:40:46 +00:00
|
|
|
import Data.List (find)
|
|
|
|
|
|
|
|
import Data.Maybe (fromJust)
|
|
|
|
|
2023-12-10 01:02:09 +00:00
|
|
|
import Network.Socket as Net
|
|
|
|
|
2023-12-09 12:58:59 +00:00
|
|
|
import System.Posix.Signals
|
|
|
|
|
|
|
|
-- internal imports
|
|
|
|
|
2023-12-10 16:00:50 +00:00
|
|
|
import Library.Types
|
|
|
|
|
2024-05-01 21:52:49 +00:00
|
|
|
import Server.Communication.Handler as S
|
|
|
|
import Server.Communication.Receive as S
|
|
|
|
import Server.Communication.Send as S
|
2023-12-10 01:02:09 +00:00
|
|
|
import Server.Types
|
2023-12-09 12:58:59 +00:00
|
|
|
|
2023-12-09 13:18:10 +00:00
|
|
|
-- | Function which determines whether the given filePath is a supported socket path and
|
|
|
|
-- subsequently creates a socket in said location.
|
|
|
|
bindSocket
|
|
|
|
:: FilePath -- ^ File Path for socket to be created (e.g.: "/tmp/wizard.sock")
|
|
|
|
-> IO Socket -- ^ resulting Socket
|
2023-12-09 12:58:59 +00:00
|
|
|
bindSocket path = do
|
|
|
|
let sockAddr = SockAddrUnix path
|
|
|
|
unless (isSupportedSockAddr sockAddr)
|
|
|
|
(error $ "invalid socket path " <> path)
|
2024-04-06 00:18:08 +00:00
|
|
|
-- removeIfExists path
|
2024-10-31 18:19:13 +00:00
|
|
|
sock <- socket AF_UNIX Stream 0
|
2023-12-09 12:58:59 +00:00
|
|
|
bind sock sockAddr
|
2023-12-10 01:02:09 +00:00
|
|
|
Net.listen sock 5
|
2023-12-09 12:58:59 +00:00
|
|
|
pure sock
|
|
|
|
|
2023-12-10 05:57:48 +00:00
|
|
|
-- | Function that installs a handler on SIGINT to terminate game
|
|
|
|
terminateGameOnSigint
|
2024-04-06 00:18:08 +00:00
|
|
|
:: Game ()
|
|
|
|
terminateGameOnSigint = do
|
2023-12-10 05:57:48 +00:00
|
|
|
sock <- asks rcMainSocket
|
|
|
|
serverState <- gets scServerState
|
2024-04-19 18:18:49 +00:00
|
|
|
clientList <- gets scClientSockets
|
2024-11-03 03:40:46 +00:00
|
|
|
queueList <- gets scClientQueues
|
2023-12-10 05:57:48 +00:00
|
|
|
void $ liftIO $ installHandler
|
2023-12-12 10:21:25 +00:00
|
|
|
sigINT
|
2023-12-09 12:58:59 +00:00
|
|
|
(CatchOnce $ do
|
2023-12-12 10:21:25 +00:00
|
|
|
putStrLn "SIGINT caught, terminating…"
|
2024-11-03 03:40:46 +00:00
|
|
|
queues <- liftIO $ STM.atomically $ STM.readTMVar queueList
|
|
|
|
disconnectClients clientList queues
|
2024-10-31 20:01:53 +00:00
|
|
|
threadDelay (10 ^ 6)
|
2023-12-10 19:12:53 +00:00
|
|
|
close sock
|
2024-04-08 01:50:12 +00:00
|
|
|
st <- STM.atomically $ STM.readTMVar serverState
|
|
|
|
void $ STM.atomically $ STM.swapTMVar serverState $ st
|
|
|
|
{ serverStop = True
|
|
|
|
}
|
2023-12-09 12:58:59 +00:00
|
|
|
)
|
2023-12-12 12:44:33 +00:00
|
|
|
Nothing
|
2024-02-02 07:16:28 +00:00
|
|
|
|
2024-03-29 09:34:15 +00:00
|
|
|
-- | Disconnect all connected clients gracefully by announcing the server quitting
|
2024-02-02 07:16:28 +00:00
|
|
|
disconnectClients
|
2024-11-03 03:40:46 +00:00
|
|
|
:: STM.TMVar [ClientSocket]
|
|
|
|
-> [ClientQueue]
|
2024-02-02 07:16:28 +00:00
|
|
|
-> IO ()
|
2024-11-03 03:40:46 +00:00
|
|
|
disconnectClients clientSockets queues = do
|
2024-04-19 18:18:49 +00:00
|
|
|
putStrLn "server shutting down. Notifying all clients…"
|
|
|
|
mapM_
|
2024-11-03 03:40:46 +00:00
|
|
|
(\queue -> do
|
2024-04-19 18:18:49 +00:00
|
|
|
maybe
|
|
|
|
(pure ())
|
|
|
|
(\uuid -> do
|
2024-11-02 22:37:15 +00:00
|
|
|
putStrLn $ "notifying client: " <> show uuid
|
2024-11-03 03:40:46 +00:00
|
|
|
queueMessage ServerQuit uuid queues
|
|
|
|
sock <- do
|
|
|
|
socketList <- STM.atomically $ STM.readTMVar clientSockets
|
|
|
|
pure $ fromJust $ find (\s -> csUUID s == Just uuid) socketList
|
|
|
|
dropClient clientSockets (csSocket sock)
|
2024-04-19 18:18:49 +00:00
|
|
|
)
|
2024-11-03 03:40:46 +00:00
|
|
|
(cqUUID queue)
|
2024-04-19 18:18:49 +00:00
|
|
|
)
|
2024-11-03 03:40:46 +00:00
|
|
|
queues
|
2023-12-10 01:02:09 +00:00
|
|
|
|
2024-02-02 14:50:57 +00:00
|
|
|
|
2023-12-10 01:02:09 +00:00
|
|
|
-- | Process incoming connection requests
|
2023-12-10 05:57:48 +00:00
|
|
|
processRequests :: Game ()
|
2023-12-10 01:02:09 +00:00
|
|
|
processRequests = do
|
|
|
|
mainSocket <- asks rcMainSocket
|
2024-11-03 03:40:46 +00:00
|
|
|
serverQueue <- gets scMessageQueue
|
2023-12-10 05:57:48 +00:00
|
|
|
socketList <- gets scClientSockets
|
2024-11-03 03:40:46 +00:00
|
|
|
queueList <- gets scClientQueues
|
2024-04-08 01:50:12 +00:00
|
|
|
st <- gets scServerState
|
2024-11-03 03:40:46 +00:00
|
|
|
void $ liftIO $ forkIO $ acceptConnection mainSocket socketList queueList serverQueue st
|
2023-12-10 05:57:48 +00:00
|
|
|
where
|
2024-11-03 03:40:46 +00:00
|
|
|
acceptConnection mainSocket socketList queueList serverQueue st = do
|
2024-04-08 00:42:07 +00:00
|
|
|
putStrLn "Ready for new connection requests…"
|
2024-04-19 18:18:49 +00:00
|
|
|
eSock <- try $ accept mainSocket
|
|
|
|
case eSock of
|
2024-04-08 01:50:12 +00:00
|
|
|
Left (_ :: SomeException) ->
|
|
|
|
putStrLn "Main socket vanished!"
|
|
|
|
Right (clientSock, _) -> do
|
2024-11-03 03:40:46 +00:00
|
|
|
clientQueue <- STM.newTQueueIO
|
2024-11-02 22:37:15 +00:00
|
|
|
sockContainer <- STM.newTMVarIO clientSock
|
2024-11-03 03:40:46 +00:00
|
|
|
receiverThreadId <- liftIO $ forkIO $ forever $ do
|
|
|
|
receiveMessage sockContainer serverQueue
|
|
|
|
senderThreadId <- liftIO $ forkIO $ forever $ do
|
|
|
|
sendMessageQueue sockContainer clientQueue
|
2024-10-31 18:19:13 +00:00
|
|
|
print clientSock
|
2024-04-08 01:50:12 +00:00
|
|
|
liftIO $ STM.atomically $ do
|
2024-11-03 03:40:46 +00:00
|
|
|
slist <- STM.takeTMVar socketList
|
2024-10-31 18:19:13 +00:00
|
|
|
STM.putTMVar
|
|
|
|
socketList
|
2024-11-03 03:40:46 +00:00
|
|
|
(ClientSocket
|
2024-10-31 18:19:13 +00:00
|
|
|
Nothing
|
|
|
|
clientSock
|
2024-11-03 03:40:46 +00:00
|
|
|
receiverThreadId
|
|
|
|
senderThreadId
|
|
|
|
: slist
|
|
|
|
)
|
|
|
|
qlist <- STM.takeTMVar queueList
|
|
|
|
STM.putTMVar queueList
|
|
|
|
(ClientQueue
|
|
|
|
Nothing
|
|
|
|
clientQueue
|
|
|
|
: qlist
|
2024-10-31 18:19:13 +00:00
|
|
|
)
|
2024-04-08 01:50:12 +00:00
|
|
|
putStrLn "accepted new connection"
|
2024-11-02 22:37:15 +00:00
|
|
|
-- abortCondition <- serverStop <$> STM.atomically (STM.readTMVar st)
|
|
|
|
-- unless abortCondition $
|
|
|
|
-- acceptConnection mainSocket socketList queue st
|
2024-11-03 03:40:46 +00:00
|
|
|
acceptConnection mainSocket socketList queueList serverQueue st
|