wizard-wipeout/src-server/Server/Communication.hs

149 lines
4.4 KiB
Haskell
Raw Normal View History

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
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)
-- 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
-- | Function that installs a handler on SIGINT to terminate game
terminateGameOnSigint
:: Game ()
terminateGameOnSigint = do
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
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 10:51:13 +00:00
disconnectClients clientList queueList
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]
2024-11-03 10:51:13 +00:00
-> STM.TMVar [ClientQueue]
2024-02-02 07:16:28 +00:00
-> IO ()
2024-11-03 10:51:13 +00:00
disconnectClients clientSockets queueList = do
2024-04-19 18:18:49 +00:00
putStrLn "server shutting down. Notifying all clients…"
2024-11-03 10:51:13 +00:00
queues <- STM.atomically $ STM.readTMVar queueList
2024-04-19 18:18:49 +00:00
mapM_
2024-11-03 03:40:46 +00:00
(\queue -> do
2024-04-19 18:18:49 +00:00
maybe
(pure ())
(\uuid -> do
putStrLn $ "notifying client: " <> show uuid
2024-11-03 10:51:13 +00:00
queueMessage ServerQuit uuid queueList
2024-11-03 03:40:46 +00:00
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
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
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
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
sockContainer <- STM.newTMVarIO clientSock
2024-11-03 10:51:13 +00:00
receiverThreadId <- liftIO $ do
t <- forkIO $ forever $ receiveMessage sockContainer serverQueue
putStrLn "enabled listener thread"
pure t
senderThreadId <- liftIO $ do
t <- forkIO $ forever $ sendMessageQueue sockContainer clientQueue
putStrLn "enabled sender thread"
pure t
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"
-- 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