nearing the solution
This commit is contained in:
parent
473d9dea6e
commit
e8bdc8a1f7
10 changed files with 181 additions and 161 deletions
|
@ -2,6 +2,6 @@ setSocketPath : "/tmp/wizard.sock"
|
||||||
setMapRows : 40
|
setMapRows : 40
|
||||||
setMapColumns : 40
|
setMapColumns : 40
|
||||||
setSpawnerProbability : 0.01
|
setSpawnerProbability : 0.01
|
||||||
setFPS : 30
|
setFPS : 1
|
||||||
setClientMaxTimeout : 5
|
setClientMaxTimeout : 5
|
||||||
setFramesPerPing : 120
|
setFramesPerPing : 120
|
||||||
|
|
|
@ -15,7 +15,7 @@ import qualified Data.Matrix as M
|
||||||
import Graphics.Vty
|
import Graphics.Vty
|
||||||
import Graphics.Vty.CrossPlatform
|
import Graphics.Vty.CrossPlatform
|
||||||
|
|
||||||
import Network.Socket (close)
|
import Network.Socket (Socket, close)
|
||||||
|
|
||||||
import Options.Applicative
|
import Options.Applicative
|
||||||
|
|
||||||
|
@ -44,8 +44,7 @@ main = do
|
||||||
mockClientState <- STM.newTMVarIO (ClientState undefined False)
|
mockClientState <- STM.newTMVarIO (ClientState undefined False)
|
||||||
let mockState = StateContainer undefined mockClientState undefined undefined
|
let mockState = StateContainer undefined mockClientState undefined undefined
|
||||||
sendMessage IdRequest sock
|
sendMessage IdRequest sock
|
||||||
receiveMessage sock messageQueue mockState
|
awaitResponse sock messageQueue mockState 0
|
||||||
awaitResponse messageQueue 1
|
|
||||||
clientIdMsg <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
clientIdMsg <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
||||||
let clientId = acClientUUID clientIdMsg
|
let clientId = acClientUUID clientIdMsg
|
||||||
putStrLn $ "received client UUID: " <> show clientId
|
putStrLn $ "received client UUID: " <> show clientId
|
||||||
|
@ -57,8 +56,7 @@ main = do
|
||||||
|
|
||||||
sendMessage (ClientMessage clientId ClientRequestWizard) sock
|
sendMessage (ClientMessage clientId ClientRequestWizard) sock
|
||||||
-- threadDelay $ 1 * 10 ^ 6
|
-- threadDelay $ 1 * 10 ^ 6
|
||||||
receiveMessage sock messageQueue mockState
|
awaitResponse sock messageQueue mockState 1
|
||||||
awaitResponse messageQueue 1
|
|
||||||
playerWizard <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
playerWizard <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
||||||
putStrLn $ "received wizard: " <> show (initWizard playerWizard)
|
putStrLn $ "received wizard: " <> show (initWizard playerWizard)
|
||||||
|
|
||||||
|
@ -85,7 +83,7 @@ main = do
|
||||||
putStrLn "Shutting down client…"
|
putStrLn "Shutting down client…"
|
||||||
showCursor (outputIface vty)
|
showCursor (outputIface vty)
|
||||||
shutdown vty
|
shutdown vty
|
||||||
threadDelay (10 ^ 6)
|
threadDelay (10 ^ 3)
|
||||||
putStrLn "Closing connection to server…"
|
putStrLn "Closing connection to server…"
|
||||||
close sock
|
close sock
|
||||||
putStrLn "bye bye"
|
putStrLn "bye bye"
|
||||||
|
@ -112,14 +110,17 @@ welcomeText = mconcat $ map (<> "\n")
|
||||||
]
|
]
|
||||||
|
|
||||||
awaitResponse
|
awaitResponse
|
||||||
:: STM.TQueue ServerMessage
|
:: Socket
|
||||||
|
-> STM.TQueue ServerMessage
|
||||||
|
-> StateContainer
|
||||||
-> Int
|
-> Int
|
||||||
-> IO ()
|
-> IO ()
|
||||||
awaitResponse _ 10 = error "Tries to communicate with server exceeded"
|
awaitResponsea _ _ _ 10 = error "Tries to communicate with server exceeded"
|
||||||
awaitResponse messageQueue numTries = do
|
awaitResponse sock messageQueue mockState numTries = do
|
||||||
|
receiveMessage sock messageQueue mockState
|
||||||
responsePresent <- not <$> STM.atomically (STM.isEmptyTQueue messageQueue)
|
responsePresent <- not <$> STM.atomically (STM.isEmptyTQueue messageQueue)
|
||||||
if responsePresent
|
if responsePresent
|
||||||
then pure ()
|
then pure ()
|
||||||
else do
|
else do
|
||||||
threadDelay $ 10 ^ 6
|
threadDelay $ 10 ^ 6
|
||||||
awaitResponse messageQueue (succ numTries)
|
awaitResponse sock messageQueue mockState (succ numTries)
|
||||||
|
|
|
@ -54,6 +54,7 @@ main = do
|
||||||
putStrLn "starting game…"
|
putStrLn "starting game…"
|
||||||
now <- getCurrentTime
|
now <- getCurrentTime
|
||||||
sockList <- STM.newTMVarIO []
|
sockList <- STM.newTMVarIO []
|
||||||
|
queueList <- STM.newTMVarIO []
|
||||||
messageQueue <- STM.newTQueueIO
|
messageQueue <- STM.newTQueueIO
|
||||||
serverState <- STM.newTMVarIO (ServerState False False)
|
serverState <- STM.newTMVarIO (ServerState False False)
|
||||||
emptyPLayers <- STM.newTMVarIO []
|
emptyPLayers <- STM.newTMVarIO []
|
||||||
|
@ -63,7 +64,7 @@ main = do
|
||||||
setFramesPerPing
|
setFramesPerPing
|
||||||
setClientMaxTimeout
|
setClientMaxTimeout
|
||||||
sock
|
sock
|
||||||
initState = StateContainer (arenaSpawners arena) emptyPLayers serverState now sockList messageQueue
|
initState = StateContainer (arenaSpawners arena) emptyPLayers serverState now sockList queueList messageQueue
|
||||||
(finalState, finalWrite) <- execRWST
|
(finalState, finalWrite) <- execRWST
|
||||||
(do
|
(do
|
||||||
terminateGameOnSigint
|
terminateGameOnSigint
|
||||||
|
|
|
@ -16,6 +16,10 @@ import Control.Monad.IO.Class
|
||||||
|
|
||||||
import Control.Monad.RWS.Strict
|
import Control.Monad.RWS.Strict
|
||||||
|
|
||||||
|
import Data.List (find)
|
||||||
|
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
|
|
||||||
import Network.Socket as Net
|
import Network.Socket as Net
|
||||||
|
|
||||||
import System.Posix.Signals
|
import System.Posix.Signals
|
||||||
|
@ -51,12 +55,13 @@ terminateGameOnSigint = do
|
||||||
sock <- asks rcMainSocket
|
sock <- asks rcMainSocket
|
||||||
serverState <- gets scServerState
|
serverState <- gets scServerState
|
||||||
clientList <- gets scClientSockets
|
clientList <- gets scClientSockets
|
||||||
|
queueList <- gets scClientQueues
|
||||||
void $ liftIO $ installHandler
|
void $ liftIO $ installHandler
|
||||||
sigINT
|
sigINT
|
||||||
(CatchOnce $ do
|
(CatchOnce $ do
|
||||||
putStrLn "SIGINT caught, terminating…"
|
putStrLn "SIGINT caught, terminating…"
|
||||||
clients <- liftIO $ STM.atomically $ STM.readTMVar clientList
|
queues <- liftIO $ STM.atomically $ STM.readTMVar queueList
|
||||||
disconnectClients clientList clients
|
disconnectClients clientList queues
|
||||||
threadDelay (10 ^ 6)
|
threadDelay (10 ^ 6)
|
||||||
close sock
|
close sock
|
||||||
st <- STM.atomically $ STM.readTMVar serverState
|
st <- STM.atomically $ STM.readTMVar serverState
|
||||||
|
@ -68,62 +73,72 @@ terminateGameOnSigint = do
|
||||||
|
|
||||||
-- | Disconnect all connected clients gracefully by announcing the server quitting
|
-- | Disconnect all connected clients gracefully by announcing the server quitting
|
||||||
disconnectClients
|
disconnectClients
|
||||||
:: STM.TMVar [ClientComms]
|
:: STM.TMVar [ClientSocket]
|
||||||
-> [ClientComms]
|
-> [ClientQueue]
|
||||||
-> IO ()
|
-> IO ()
|
||||||
disconnectClients clientList clients = do
|
disconnectClients clientSockets queues = do
|
||||||
putStrLn "server shutting down. Notifying all clients…"
|
putStrLn "server shutting down. Notifying all clients…"
|
||||||
mapM_
|
mapM_
|
||||||
(\client -> do
|
(\queue -> do
|
||||||
maybe
|
maybe
|
||||||
(pure ())
|
(pure ())
|
||||||
(\uuid -> do
|
(\uuid -> do
|
||||||
putStrLn $ "notifying client: " <> show uuid
|
putStrLn $ "notifying client: " <> show uuid
|
||||||
queueMessage ServerQuit uuid clientList
|
queueMessage ServerQuit uuid queues
|
||||||
dropClient clientList (ccSocket client)
|
sock <- do
|
||||||
|
socketList <- STM.atomically $ STM.readTMVar clientSockets
|
||||||
|
pure $ fromJust $ find (\s -> csUUID s == Just uuid) socketList
|
||||||
|
dropClient clientSockets (csSocket sock)
|
||||||
)
|
)
|
||||||
(ccUUID client)
|
(cqUUID queue)
|
||||||
)
|
)
|
||||||
clients
|
queues
|
||||||
|
|
||||||
|
|
||||||
-- | Process incoming connection requests
|
-- | Process incoming connection requests
|
||||||
processRequests :: Game ()
|
processRequests :: Game ()
|
||||||
processRequests = do
|
processRequests = do
|
||||||
mainSocket <- asks rcMainSocket
|
mainSocket <- asks rcMainSocket
|
||||||
queue <- gets scMessageQueue
|
serverQueue <- gets scMessageQueue
|
||||||
socketList <- gets scClientSockets
|
socketList <- gets scClientSockets
|
||||||
|
queueList <- gets scClientQueues
|
||||||
st <- gets scServerState
|
st <- gets scServerState
|
||||||
void $ liftIO $ forkIO $ acceptConnection mainSocket socketList queue st
|
void $ liftIO $ forkIO $ acceptConnection mainSocket socketList queueList serverQueue st
|
||||||
where
|
where
|
||||||
acceptConnection mainSocket socketList queue st = do
|
acceptConnection mainSocket socketList queueList serverQueue st = do
|
||||||
putStrLn "Ready for new connection requests…"
|
putStrLn "Ready for new connection requests…"
|
||||||
eSock <- try $ accept mainSocket
|
eSock <- try $ accept mainSocket
|
||||||
case eSock of
|
case eSock of
|
||||||
Left (_ :: SomeException) ->
|
Left (_ :: SomeException) ->
|
||||||
putStrLn "Main socket vanished!"
|
putStrLn "Main socket vanished!"
|
||||||
Right (clientSock, _) -> do
|
Right (clientSock, _) -> do
|
||||||
messageQueue <- STM.newTQueueIO
|
clientQueue <- STM.newTQueueIO
|
||||||
sockContainer <- STM.newTMVarIO clientSock
|
sockContainer <- STM.newTMVarIO clientSock
|
||||||
clientWriteThreadId <- liftIO $ forkIO $ forever $ do
|
receiverThreadId <- liftIO $ forkIO $ forever $ do
|
||||||
receiveMessage sockContainer queue socketList
|
receiveMessage sockContainer serverQueue
|
||||||
clientListenThreadId <- liftIO $ forkIO $ forever $ do
|
senderThreadId <- liftIO $ forkIO $ forever $ do
|
||||||
sendMessageQueue sockContainer messageQueue
|
sendMessageQueue sockContainer clientQueue
|
||||||
print clientSock
|
print clientSock
|
||||||
liftIO $ STM.atomically $ do
|
liftIO $ STM.atomically $ do
|
||||||
list <- STM.takeTMVar socketList
|
slist <- STM.takeTMVar socketList
|
||||||
STM.putTMVar
|
STM.putTMVar
|
||||||
socketList
|
socketList
|
||||||
(ClientComms
|
(ClientSocket
|
||||||
Nothing
|
Nothing
|
||||||
clientSock
|
clientSock
|
||||||
messageQueue
|
receiverThreadId
|
||||||
clientWriteThreadId
|
senderThreadId
|
||||||
clientListenThreadId
|
: slist
|
||||||
: list
|
)
|
||||||
|
qlist <- STM.takeTMVar queueList
|
||||||
|
STM.putTMVar queueList
|
||||||
|
(ClientQueue
|
||||||
|
Nothing
|
||||||
|
clientQueue
|
||||||
|
: qlist
|
||||||
)
|
)
|
||||||
putStrLn "accepted new connection"
|
putStrLn "accepted new connection"
|
||||||
-- abortCondition <- serverStop <$> STM.atomically (STM.readTMVar st)
|
-- abortCondition <- serverStop <$> STM.atomically (STM.readTMVar st)
|
||||||
-- unless abortCondition $
|
-- unless abortCondition $
|
||||||
-- acceptConnection mainSocket socketList queue st
|
-- acceptConnection mainSocket socketList queue st
|
||||||
acceptConnection mainSocket socketList queue st
|
acceptConnection mainSocket socketList queueList serverQueue st
|
||||||
|
|
|
@ -29,19 +29,20 @@ handleMessage
|
||||||
-> IO ()
|
-> IO ()
|
||||||
handleMessage stateContainer readerContainer msg = do
|
handleMessage stateContainer readerContainer msg = do
|
||||||
let clientList = scClientSockets stateContainer
|
let clientList = scClientSockets stateContainer
|
||||||
|
queueList <- STM.atomically $ STM.readTMVar $ scClientQueues stateContainer
|
||||||
clients <- liftIO $ STM.atomically $ STM.readTMVar clientList
|
clients <- liftIO $ STM.atomically $ STM.readTMVar clientList
|
||||||
-- putStrLn $ "Handling: " <> show msg
|
-- putStrLn $ "Handling: " <> show msg
|
||||||
case msg of
|
case msg of
|
||||||
IdRequest -> do
|
IdRequest -> do
|
||||||
clientId <- nextRandom
|
clientId <- nextRandom
|
||||||
let clientIdx = findIndex (isNothing . ccUUID) clients
|
let clientIdx = findIndex (isNothing . csUUID) clients
|
||||||
clientSock = ccSocket $ clients !! fromJust clientIdx
|
clientSock = csSocket $ clients !! fromJust clientIdx
|
||||||
newClients = map
|
newClients = map
|
||||||
(\old@(ClientComms mUUID oldClientSock _ _ _) ->
|
(\old@(ClientSocket mUUID oldClientSock _ _) ->
|
||||||
if oldClientSock == clientSock && isNothing mUUID
|
if oldClientSock == clientSock && isNothing mUUID
|
||||||
then
|
then
|
||||||
old
|
old
|
||||||
{ ccUUID = Just clientId
|
{ csUUID = Just clientId
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
old
|
old
|
||||||
|
@ -49,13 +50,13 @@ handleMessage stateContainer readerContainer msg = do
|
||||||
clients
|
clients
|
||||||
void $ liftIO $ STM.atomically $ STM.swapTMVar clientList newClients
|
void $ liftIO $ STM.atomically $ STM.swapTMVar clientList newClients
|
||||||
putStrLn $ "Accepted Client with UUID " <> show clientId
|
putStrLn $ "Accepted Client with UUID " <> show clientId
|
||||||
queueMessage (AcceptClient clientId) clientId clientList
|
queueMessage (AcceptClient clientId) clientId queueList
|
||||||
ClientMessage clientId payload ->
|
ClientMessage clientId payload ->
|
||||||
case payload of
|
case payload of
|
||||||
ClientQuit -> do
|
ClientQuit -> do
|
||||||
putStrLn $ "client has quit the game: " <> show clientId
|
putStrLn $ "client has quit the game: " <> show clientId
|
||||||
let client = find (\a -> ccUUID a == Just clientId) clients
|
let client = find (\a -> csUUID a == Just clientId) clients
|
||||||
dropClient clientList (ccSocket $ fromJust client)
|
dropClient clientList (csSocket $ fromJust client)
|
||||||
ClientRequestWizard -> do
|
ClientRequestWizard -> do
|
||||||
putStrLn "initializing new wizard"
|
putStrLn "initializing new wizard"
|
||||||
let arena = rcMap readerContainer
|
let arena = rcMap readerContainer
|
||||||
|
@ -67,9 +68,8 @@ handleMessage stateContainer readerContainer msg = do
|
||||||
currentPlayers <- STM.readTMVar (scPlayers stateContainer)
|
currentPlayers <- STM.readTMVar (scPlayers stateContainer)
|
||||||
void $ STM.swapTMVar (scPlayers stateContainer) $
|
void $ STM.swapTMVar (scPlayers stateContainer) $
|
||||||
Player clientId freshWizard False (now, uuid) : currentPlayers
|
Player clientId freshWizard False (now, uuid) : currentPlayers
|
||||||
queueMessage (ProvideInitialWizard freshWizard) clientId clientList
|
queueMessage (ProvideInitialWizard freshWizard) clientId queueList
|
||||||
ClientReady -> do
|
ClientReady -> do
|
||||||
putStrLn $ "client " <> show clientId <> " is ready!"
|
|
||||||
now <- getCurrentTime
|
now <- getCurrentTime
|
||||||
STM.atomically $ do
|
STM.atomically $ do
|
||||||
currentPlayers <- STM.readTMVar (scPlayers stateContainer)
|
currentPlayers <- STM.readTMVar (scPlayers stateContainer)
|
||||||
|
@ -82,27 +82,27 @@ handleMessage stateContainer readerContainer msg = do
|
||||||
, playerLastPong = (now, snd (playerLastPong $ head thisPlayers))
|
, playerLastPong = (now, snd (playerLastPong $ head thisPlayers))
|
||||||
}
|
}
|
||||||
: otherPlayers
|
: otherPlayers
|
||||||
Pong uuid -> do
|
putStrLn $ "client ready: " <> show clientId
|
||||||
let mclient = find (\c -> ccUUID c == Just clientId) clients
|
Pong pongUuid -> do
|
||||||
|
let mclient = find (\c -> csUUID c == Just clientId) clients
|
||||||
|
players <- STM.atomically $ STM.readTMVar (scPlayers stateContainer)
|
||||||
maybe
|
maybe
|
||||||
(putStrLn $ "Who is " <> show uuid <> "?")
|
(putStrLn $ "Unknown client: " <> show clientId)
|
||||||
(\_ -> do
|
(const $ do
|
||||||
mPlayer <- STM.atomically $ do
|
mPlayer <- STM.atomically $ do
|
||||||
players <- STM.readTMVar (scPlayers stateContainer)
|
|
||||||
pure $ find (\p -> playerId p == clientId) players
|
pure $ find (\p -> playerId p == clientId) players
|
||||||
maybe
|
maybe
|
||||||
(pure ())
|
(pure ())
|
||||||
(\player ->
|
(\player ->
|
||||||
if snd (playerLastPong player) /= uuid
|
if snd (playerLastPong player) /= pongUuid
|
||||||
then
|
then
|
||||||
putStrLn $ "Pong ID mismatch from " <> show clientId
|
putStrLn $ "Pong ID mismatch from: " <> show clientId
|
||||||
else do
|
else do
|
||||||
now <- getCurrentTime
|
now <- getCurrentTime
|
||||||
STM.atomically$ do
|
STM.atomically$ do
|
||||||
players <- STM.readTMVar (scPlayers stateContainer)
|
|
||||||
let otherPlayers = filter (\a -> playerId a /= playerId player) players
|
let otherPlayers = filter (\a -> playerId a /= playerId player) players
|
||||||
modPlayer = player
|
modPlayer = player
|
||||||
{ playerLastPong = (now, uuid)
|
{ playerLastPong = (now, pongUuid)
|
||||||
}
|
}
|
||||||
void $ STM.swapTMVar (scPlayers stateContainer) (modPlayer : otherPlayers)
|
void $ STM.swapTMVar (scPlayers stateContainer) (modPlayer : otherPlayers)
|
||||||
sendUpdate stateContainer (rcMap readerContainer) player
|
sendUpdate stateContainer (rcMap readerContainer) player
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module Server.Communication.Receive where
|
module Server.Communication.Receive where
|
||||||
|
|
||||||
import Control.Concurrent (threadDelay)
|
import Control.Concurrent (threadDelay)
|
||||||
|
@ -30,43 +31,38 @@ import Server.Types
|
||||||
receiveMessage
|
receiveMessage
|
||||||
:: STM.TMVar Socket
|
:: STM.TMVar Socket
|
||||||
-> STM.TQueue ClientMessage
|
-> STM.TQueue ClientMessage
|
||||||
-> STM.TMVar [ClientComms]
|
|
||||||
-> IO ()
|
-> IO ()
|
||||||
receiveMessage sockContainer queue clientList = do
|
receiveMessage sockContainer queue = do
|
||||||
randSleep <- randomRIO (1, 1000)
|
randSleep <- randomRIO (1, 1000)
|
||||||
threadDelay randSleep
|
threadDelay randSleep
|
||||||
msock <- STM.atomically $ STM.tryTakeTMVar sockContainer
|
sock <- STM.atomically $ STM.readTMVar sockContainer
|
||||||
let maxBufferLength = 4096
|
let maxBufferLength = 4096
|
||||||
|
putStrLn "read socket container for receiving"
|
||||||
|
mMsg <- do
|
||||||
|
ptr <- mallocArray maxBufferLength
|
||||||
|
putStrLn "receiving data"
|
||||||
|
eBufferLength <-
|
||||||
|
try $ recvBuf sock ptr maxBufferLength
|
||||||
|
bufferLength <- case eBufferLength of
|
||||||
|
Left (e :: IOException) -> do
|
||||||
|
-- putStrLn ("Socket vanished, cleaning up after " <> show e)
|
||||||
|
-- dropClient clientList sock
|
||||||
|
pure 0
|
||||||
|
Right len -> pure len
|
||||||
|
free ptr
|
||||||
|
msg <- B.pack <$> peekArray bufferLength ptr
|
||||||
|
putStrLn $ "received data: " <> show msg
|
||||||
|
if bufferLength > 0 && msg /= ""
|
||||||
|
then do
|
||||||
|
putStrLn $ "received message: " <> show msg
|
||||||
|
pure (A.decode' $ B8.fromStrict msg :: Maybe ClientMessage)
|
||||||
|
else
|
||||||
|
pure Nothing
|
||||||
maybe
|
maybe
|
||||||
(pure ())
|
(pure ())
|
||||||
(\sock -> do
|
(\msg -> do
|
||||||
putStrLn "took socket container for receiving"
|
print msg
|
||||||
mMsg <- do
|
liftIO $ STM.atomically $ STM.writeTQueue queue msg
|
||||||
ptr <- mallocArray maxBufferLength
|
-- when (msg == IdRequest) (threadDelay $ 10 ^ 3)
|
||||||
eBufferLength <-
|
|
||||||
try $ recvBuf sock ptr maxBufferLength
|
|
||||||
bufferLength <- case eBufferLength of
|
|
||||||
Left (e :: IOException) -> do
|
|
||||||
-- putStrLn ("Socket vanished, cleaning up after " <> show e)
|
|
||||||
-- dropClient clientList sock
|
|
||||||
pure 0
|
|
||||||
Right len -> pure len
|
|
||||||
msg <- B.pack <$> peekArray bufferLength ptr
|
|
||||||
let mJsonMsg = A.decode' $ B8.fromStrict msg :: Maybe ClientMessage
|
|
||||||
free ptr
|
|
||||||
if bufferLength > 0
|
|
||||||
then
|
|
||||||
pure mJsonMsg
|
|
||||||
else
|
|
||||||
pure Nothing
|
|
||||||
maybe
|
|
||||||
(pure ())
|
|
||||||
(\msg -> do
|
|
||||||
print msg
|
|
||||||
liftIO $ STM.atomically $ STM.writeTQueue queue msg
|
|
||||||
-- when (msg == IdRequest) (threadDelay $ 10 ^ 3)
|
|
||||||
)
|
|
||||||
mMsg
|
|
||||||
STM.atomically $ STM.putTMVar sockContainer sock
|
|
||||||
)
|
)
|
||||||
msock
|
mMsg
|
||||||
|
|
|
@ -47,50 +47,44 @@ import Server.Types
|
||||||
queueMessage
|
queueMessage
|
||||||
:: ServerMessage
|
:: ServerMessage
|
||||||
-> UUID
|
-> UUID
|
||||||
-> STM.TMVar [ClientComms]
|
-> [ClientQueue]
|
||||||
-> IO ()
|
-> IO ()
|
||||||
queueMessage msg uuid clientList = do
|
queueMessage msg uuid queueList = do
|
||||||
clients <- STM.atomically $ STM.readTMVar clientList
|
putStrLn $ "queueing message \"" <> show msg <> "\" for client " <> show uuid <> " …"
|
||||||
let mQueue = ccQueue <$> find (\client -> ccUUID client == Just uuid) clients
|
let mQueue = cqQueue <$> find (\client -> cqUUID client == Just uuid) queueList
|
||||||
maybe
|
maybe
|
||||||
(putStrLn $ "unknown client UUID: " <> show uuid)
|
(putStrLn $ "no queue found for client: " <> show uuid)
|
||||||
(\queue ->
|
(\queue ->
|
||||||
STM.atomically $ STM.writeTQueue queue msg
|
STM.atomically $ STM.writeTQueue queue msg
|
||||||
)
|
)
|
||||||
mQueue
|
mQueue
|
||||||
|
putStrLn "message queued!"
|
||||||
|
|
||||||
sendMessageQueue
|
sendMessageQueue
|
||||||
:: STM.TMVar Socket
|
:: STM.TMVar Socket
|
||||||
-> STM.TQueue ServerMessage
|
-> STM.TQueue ServerMessage
|
||||||
-> IO ()
|
-> IO ()
|
||||||
sendMessageQueue sockContainer queue = do
|
sendMessageQueue sockContainer queue = do
|
||||||
randSleep <- randomRIO (1, 1000)
|
msgs <- STM.atomically $ STM.flushTQueue queue
|
||||||
threadDelay randSleep
|
mapM_
|
||||||
msock <- STM.atomically $ STM.tryTakeTMVar sockContainer
|
(\msg -> do
|
||||||
maybe
|
sock <- STM.atomically $ STM.readTMVar sockContainer
|
||||||
(pure ())
|
putStrLn "read socket container for sending"
|
||||||
(\sock -> do
|
putStrLn $ "sending message: " <> show msg
|
||||||
putStrLn "took socket container for sending"
|
let msgJson = A.encode msg
|
||||||
msgs <- STM.atomically $ STM.flushTQueue queue
|
msgVector = VS.fromList $ B.unpack $ B.toStrict ('<' `B8.cons` (msgJson `B8.snoc` '>'))
|
||||||
mapM_
|
VS.unsafeWith
|
||||||
(\msg -> do
|
msgVector
|
||||||
let msgJson = A.encode msg
|
(\ptr -> do
|
||||||
msgVector = VS.fromList $ B.unpack $ B.toStrict ('<' `B8.cons` (msgJson `B8.snoc` '>'))
|
eResult <- try $ void $ sendBuf sock ptr (VS.length msgVector)
|
||||||
VS.unsafeWith
|
case eResult of
|
||||||
msgVector
|
Left (e :: IOException) ->
|
||||||
(\ptr -> do
|
putStrLn $ "can't reach client after " <> show e
|
||||||
eResult <- try $ void $ sendBuf sock ptr (VS.length msgVector)
|
Right _ ->
|
||||||
case eResult of
|
pure ()
|
||||||
Left (e :: IOException) ->
|
|
||||||
putStrLn $ "can't reach client after " <> show e
|
|
||||||
Right _ ->
|
|
||||||
pure ()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
msgs
|
|
||||||
STM.atomically $ STM.putTMVar sockContainer sock
|
|
||||||
)
|
)
|
||||||
msock
|
msgs
|
||||||
|
|
||||||
sendPings :: Game ()
|
sendPings :: Game ()
|
||||||
sendPings = do
|
sendPings = do
|
||||||
|
@ -100,54 +94,59 @@ sendPings = do
|
||||||
fps <- asks rcFPS
|
fps <- asks rcFPS
|
||||||
stateContainer <- get
|
stateContainer <- get
|
||||||
players <- liftIO $ STM.atomically $ STM.readTMVar (scPlayers stateContainer)
|
players <- liftIO $ STM.atomically $ STM.readTMVar (scPlayers stateContainer)
|
||||||
sockets <- liftIO $ STM.atomically $ STM.readTMVar (scClientSockets stateContainer)
|
-- sockets <- liftIO $ STM.atomically $ STM.readTMVar (scClientSockets stateContainer)
|
||||||
mapM_
|
mapM_
|
||||||
(\player@(Player plId (Wizard {}) readiness (lastPongTime, _)) -> do
|
(\player@(Player plId (Wizard {}) readiness (lastPongTime, _)) -> do
|
||||||
let timeDiff = realToFrac $ diffUTCTime now lastPongTime :: Float
|
let timeDiff = realToFrac $ diffUTCTime now lastPongTime :: Float
|
||||||
when (readiness && timeDiff > (fromIntegral framesPerPing * recip (fromIntegral fps))) $ do
|
when (readiness && timeDiff > (fromIntegral framesPerPing * recip (fromIntegral fps))) $ do
|
||||||
let clientSock = ccSocket <$> find (\c -> ccUUID c == Just plId) sockets
|
-- let clientSock = ccSocket <$> find (\c -> ccUUID c == Just plId) sockets
|
||||||
when (isJust clientSock) $
|
-- when (isJust clientSock) $
|
||||||
if timeDiff > realToFrac maxTimeout
|
-- if timeDiff > realToFrac maxTimeout
|
||||||
then do
|
-- then do
|
||||||
liftIO $ do
|
-- liftIO $ do
|
||||||
putStrLn $ "dropping client because of timeout: " <> show plId
|
-- putStrLn $ "dropping client because of timeout: " <> show plId
|
||||||
dropClient (scClientSockets stateContainer) (fromJust clientSock)
|
-- dropClient (scClientSockets stateContainer) (fromJust clientSock)
|
||||||
put stateContainer
|
-- put stateContainer
|
||||||
else do
|
-- else do
|
||||||
random <- liftIO nextRandom
|
random <- liftIO nextRandom
|
||||||
let newPong = (now, random)
|
let newPong = (now, random)
|
||||||
liftIO $ queueMessage
|
liftIO $ do
|
||||||
( Ping random
|
queues <- STM.atomically $ STM.readTMVar (scClientQueues stateContainer)
|
||||||
)
|
queueMessage
|
||||||
plId
|
( Ping random
|
||||||
(scClientSockets stateContainer)
|
)
|
||||||
|
plId
|
||||||
|
queues
|
||||||
let newPlayer = player
|
let newPlayer = player
|
||||||
{ playerLastPong = newPong
|
{ playerLastPong = newPong
|
||||||
}
|
}
|
||||||
otherPlayers = filter (\a -> playerId a /= plId) players
|
otherPlayers = filter (\a -> playerId a /= plId) players
|
||||||
liftIO $ void $ STM.atomically $
|
liftIO $ do
|
||||||
STM.swapTMVar (scPlayers stateContainer) (newPlayer : otherPlayers)
|
void $ STM.atomically $
|
||||||
|
STM.swapTMVar (scPlayers stateContainer) (newPlayer : otherPlayers)
|
||||||
|
putStrLn "ping!"
|
||||||
)
|
)
|
||||||
players
|
players
|
||||||
put stateContainer
|
put stateContainer
|
||||||
|
|
||||||
-- | Drops the client from internal management and closes its socket, if still present.
|
-- | Drops the client from internal management and closes its socket, if still present.
|
||||||
dropClient
|
dropClient
|
||||||
:: STM.TMVar [ClientComms]
|
:: STM.TMVar [ClientSocket]
|
||||||
-> Socket
|
-> Socket
|
||||||
-> IO ()
|
-> IO ()
|
||||||
dropClient clientList sock = do
|
dropClient clientList sock = do
|
||||||
mClient <- STM.atomically $ do
|
mClient <- STM.atomically $ do
|
||||||
clients <- STM.readTMVar clientList
|
clients <- STM.readTMVar clientList
|
||||||
let mclient = find (\client -> ccSocket client == sock) clients
|
let mclient = find (\client -> csSocket client == sock) clients
|
||||||
let reducedClients = filter (\client -> ccSocket client /= sock) clients
|
let reducedClients = filter (\client -> csSocket client /= sock) clients
|
||||||
void $ STM.swapTMVar clientList reducedClients
|
void $ STM.swapTMVar clientList reducedClients
|
||||||
pure mclient
|
pure mclient
|
||||||
maybe
|
maybe
|
||||||
(putStrLn $ "closing unknown socket: " <> show sock)
|
(putStrLn $ "closing unknown socket: " <> show sock)
|
||||||
(\client -> do
|
(\client -> do
|
||||||
putStrLn $ "dropping client because of closed socket: " <> show (fromJust $ ccUUID client)
|
putStrLn $ "dropping client because of closed socket: " <> show (fromJust $ csUUID client)
|
||||||
killThread (ccListener client)
|
killThread (csSender client)
|
||||||
|
killThread (csReceiver client)
|
||||||
)
|
)
|
||||||
mClient
|
mClient
|
||||||
close sock
|
close sock
|
||||||
|
@ -158,11 +157,11 @@ sendUpdate
|
||||||
-> Player
|
-> Player
|
||||||
-> IO ()
|
-> IO ()
|
||||||
sendUpdate stateContainer tileMap player = do
|
sendUpdate stateContainer tileMap player = do
|
||||||
slice <- buildSlice player
|
let slice = buildSlice player
|
||||||
sendSlice slice player
|
sendSlice slice player
|
||||||
where
|
where
|
||||||
buildSlice :: Player -> IO MapSlice
|
buildSlice :: Player -> MapSlice
|
||||||
buildSlice (Player _ (Wizard {..}) _ _) = do
|
buildSlice (Player _ (Wizard {..}) _ _) =
|
||||||
let V2 wr wc = wizardPos
|
let V2 wr wc = wizardPos
|
||||||
subCoords = (,) <$> [floor wr - 4 .. floor wr + 4] <*> [floor wc - 4 .. floor wc + 4]
|
subCoords = (,) <$> [floor wr - 4 .. floor wr + 4] <*> [floor wc - 4 .. floor wc + 4]
|
||||||
leftBound = wizardRot + (pi / 4)
|
leftBound = wizardRot + (pi / 4)
|
||||||
|
@ -185,9 +184,12 @@ sendUpdate stateContainer tileMap player = do
|
||||||
Nothing
|
Nothing
|
||||||
)
|
)
|
||||||
subCoords
|
subCoords
|
||||||
pure (MapSlice initViewMatrix [])
|
in
|
||||||
|
MapSlice initViewMatrix []
|
||||||
sendSlice :: MapSlice -> Player -> IO ()
|
sendSlice :: MapSlice -> Player -> IO ()
|
||||||
sendSlice slice (Player playerId wizard _ _) = do
|
sendSlice slice (Player playerId wizard _ _) = do
|
||||||
let msg = TickUpdate slice wizard
|
let msg = TickUpdate slice wizard
|
||||||
-- print slice
|
-- print slice
|
||||||
liftIO $ queueMessage msg playerId (scClientSockets stateContainer)
|
liftIO $ do
|
||||||
|
queues <- STM.atomically $ STM.readTMVar $ scClientQueues stateContainer
|
||||||
|
queueMessage msg playerId queues
|
||||||
|
|
|
@ -36,13 +36,13 @@ runGame = do
|
||||||
let delta = realToFrac $ diffUTCTime now before
|
let delta = realToFrac $ diffUTCTime now before
|
||||||
-- liftIO $ B8.hPutStr stdout $ "tick: " <> fromString (show delta) <> " \r"
|
-- liftIO $ B8.hPutStr stdout $ "tick: " <> fromString (show delta) <> " \r"
|
||||||
handleMessages
|
handleMessages
|
||||||
updateSpawners delta
|
-- updateSpawners delta
|
||||||
updateWizards delta
|
-- updateWizards delta
|
||||||
modify'
|
-- modify'
|
||||||
(\s -> s
|
-- (\s -> s
|
||||||
{ scServerLastTick = now
|
-- { scServerLastTick = now
|
||||||
}
|
-- }
|
||||||
)
|
-- )
|
||||||
fps <- asks rcFPS
|
fps <- asks rcFPS
|
||||||
sendPings
|
sendPings
|
||||||
let remainingTime = recip (fromIntegral fps) - delta
|
let remainingTime = recip (fromIntegral fps) - delta
|
||||||
|
@ -50,4 +50,3 @@ runGame = do
|
||||||
liftIO $ threadDelay $ floor $ remainingTime * 10 ^ 6
|
liftIO $ threadDelay $ floor $ remainingTime * 10 ^ 6
|
||||||
)
|
)
|
||||||
liftIO $ threadDelay (10 ^ 6)
|
liftIO $ threadDelay (10 ^ 6)
|
||||||
-- liftIO $ killThread recvThread
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Server.Types
|
||||||
|
|
||||||
updateSpawners :: Float -> Game ()
|
updateSpawners :: Float -> Game ()
|
||||||
updateSpawners dt =
|
updateSpawners dt =
|
||||||
modify' (\sc@(StateContainer spawners _ _ _ _ _) ->
|
modify' (\sc@(StateContainer spawners _ _ _ _ _ _) ->
|
||||||
let newSpawners = map
|
let newSpawners = map
|
||||||
(\spawner -> do
|
(\spawner -> do
|
||||||
let newTTL = spawnerReloadTTL spawner - dt
|
let newTTL = spawnerReloadTTL spawner - dt
|
||||||
|
|
|
@ -63,16 +63,22 @@ data StateContainer = StateContainer
|
||||||
, scPlayers :: STM.TMVar [Player]
|
, scPlayers :: STM.TMVar [Player]
|
||||||
, scServerState :: STM.TMVar ServerState
|
, scServerState :: STM.TMVar ServerState
|
||||||
, scServerLastTick :: UTCTime
|
, scServerLastTick :: UTCTime
|
||||||
, scClientSockets :: STM.TMVar [ClientComms]
|
, scClientSockets :: STM.TMVar [ClientSocket]
|
||||||
|
, scClientQueues :: STM.TMVar [ClientQueue]
|
||||||
, scMessageQueue :: STM.TQueue ClientMessage
|
, scMessageQueue :: STM.TQueue ClientMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
data ClientComms = ClientComms
|
data ClientSocket = ClientSocket
|
||||||
{ ccUUID :: Maybe UUID
|
{ csUUID :: Maybe UUID
|
||||||
, ccSocket :: Socket
|
, csSocket :: Socket
|
||||||
, ccQueue :: STM.TQueue ServerMessage
|
, csSender :: ThreadId
|
||||||
, ccWriter :: ThreadId
|
, csReceiver :: ThreadId
|
||||||
, ccListener :: ThreadId
|
}
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
data ClientQueue = ClientQueue
|
||||||
|
{ cqUUID :: Maybe UUID
|
||||||
|
, cqQueue :: STM.TQueue ServerMessage
|
||||||
}
|
}
|
||||||
deriving (Eq)
|
deriving (Eq)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue