make client logging sensible
This commit is contained in:
parent
892333bb6e
commit
bbf60abc59
8 changed files with 125 additions and 61 deletions
|
@ -28,6 +28,7 @@ import System.Posix.Signals
|
||||||
-- internal imports
|
-- internal imports
|
||||||
|
|
||||||
import Library.Types
|
import Library.Types
|
||||||
|
import Client.Log
|
||||||
import Client.Types
|
import Client.Types
|
||||||
import GHC.Conc (threadDelay)
|
import GHC.Conc (threadDelay)
|
||||||
|
|
||||||
|
@ -42,17 +43,18 @@ connectSocket path = do
|
||||||
|
|
||||||
-- | Sends a specified message through given socket to the server
|
-- | Sends a specified message through given socket to the server
|
||||||
sendMessage
|
sendMessage
|
||||||
:: ClientMessage
|
:: LogLevel
|
||||||
|
-> ClientMessage
|
||||||
-> Socket
|
-> Socket
|
||||||
-> IO ()
|
-> IO ()
|
||||||
sendMessage msg sock = do
|
sendMessage curLevel msg sock = do
|
||||||
putStrLn $ "sending message: " <> show msg
|
logPrintIO curLevel Verbose $ "sending message: " <> show msg
|
||||||
let msgJson = ('<' `B8.cons` A.encode msg) `B8.snoc` '>'
|
let msgJson = ('<' `B8.cons` A.encode msg) `B8.snoc` '>'
|
||||||
msgList = B.unpack $ B.toStrict msgJson
|
msgList = B.unpack $ B.toStrict msgJson
|
||||||
putStrLn $ "sending raw message buffer: " <> show msgList
|
logPrintIO curLevel Verbose $ "sending raw message buffer: " <> show msgList
|
||||||
ptr <- newArray msgList
|
ptr <- newArray msgList
|
||||||
void $ sendBuf sock ptr (length msgList)
|
void $ sendBuf sock ptr (length msgList)
|
||||||
putStrLn "raw message buffer sent"
|
logPrintIO curLevel Verbose "raw message buffer sent"
|
||||||
free ptr
|
free ptr
|
||||||
|
|
||||||
handleMessages
|
handleMessages
|
||||||
|
@ -69,14 +71,16 @@ handleMessage
|
||||||
-> Game ()
|
-> Game ()
|
||||||
handleMessage ServerQuit = do
|
handleMessage ServerQuit = do
|
||||||
st <- get
|
st <- get
|
||||||
liftIO $ do
|
logPrint Info "Quitting due to server shutdown"
|
||||||
putStrLn "Quitting due to server shutdown"
|
liftIO $
|
||||||
writeIORef (scStopper st) True
|
writeIORef (scStopper st) True
|
||||||
|
|
||||||
handleMessage (Ping id') = do
|
handleMessage (Ping id') = do
|
||||||
cid <- asks rcClientUUID
|
cid <- asks rcClientUUID
|
||||||
sock <- asks rcSocket
|
sock <- asks rcSocket
|
||||||
|
curLevel <- asks rcLogLevel
|
||||||
liftIO $ sendMessage
|
liftIO $ sendMessage
|
||||||
|
curLevel
|
||||||
( ClientMessage
|
( ClientMessage
|
||||||
cid
|
cid
|
||||||
(Pong id')
|
(Pong id')
|
||||||
|
@ -92,30 +96,32 @@ handleMessage (TickUpdate !slice !wizard) = do
|
||||||
put newState
|
put newState
|
||||||
|
|
||||||
handleMessage x =
|
handleMessage x =
|
||||||
liftIO $ putStrLn $ "received unexpected message from server: " <> show x
|
logPrint Warning $ "received unexpected message from server: " <> show x
|
||||||
|
|
||||||
-- Gracefully shut down the client with an error message
|
-- Gracefully shut down the client with an error message
|
||||||
gracefulExit
|
gracefulExit
|
||||||
:: StateContainer
|
:: LogLevel
|
||||||
|
-> StateContainer
|
||||||
-> String
|
-> String
|
||||||
-> IO ()
|
-> IO ()
|
||||||
gracefulExit st reason = do
|
gracefulExit curLevel st reason = do
|
||||||
liftIO $ putStrLn reason
|
logPrintIO curLevel Info reason
|
||||||
writeIORef (scStopper st) True
|
writeIORef (scStopper st) True
|
||||||
|
|
||||||
receiveMessage
|
receiveMessage
|
||||||
:: Socket
|
:: LogLevel
|
||||||
|
-> Socket
|
||||||
-> STM.TQueue ServerMessage
|
-> STM.TQueue ServerMessage
|
||||||
-> StateContainer
|
-> StateContainer
|
||||||
-> IO ()
|
-> IO ()
|
||||||
receiveMessage sock queue st = do
|
receiveMessage curLevel sock queue st = do
|
||||||
let maxBufferLength = 4096
|
let maxBufferLength = 4096
|
||||||
ptr <- mallocArray maxBufferLength
|
ptr <- mallocArray maxBufferLength
|
||||||
ebufferLength <- try $ recvBuf sock ptr maxBufferLength
|
ebufferLength <- try $ recvBuf sock ptr maxBufferLength
|
||||||
-- putStrLn $ "received buffer of length: " <> show ebufferLength
|
-- putStrLn $ "received buffer of length: " <> show ebufferLength
|
||||||
bufferLength <- case ebufferLength of
|
bufferLength <- case ebufferLength of
|
||||||
Left (_ :: IOException) -> do
|
Left (_ :: IOException) -> do
|
||||||
gracefulExit st "Quitting due to connection loss"
|
gracefulExit curLevel st "Quitting due to connection loss"
|
||||||
pure 0
|
pure 0
|
||||||
Right len -> pure len
|
Right len -> pure len
|
||||||
rawMsg <- B.pack <$> peekArray bufferLength ptr
|
rawMsg <- B.pack <$> peekArray bufferLength ptr
|
||||||
|
@ -129,7 +135,7 @@ receiveMessage sock queue st = do
|
||||||
(\msg -> do
|
(\msg -> do
|
||||||
let mJsonMsg = A.decode' msg
|
let mJsonMsg = A.decode' msg
|
||||||
maybe
|
maybe
|
||||||
(putStrLn $ "received garbled data: " <> B8.unpack (B8.fromStrict rawMsg))
|
(logPrintIO curLevel Warning $ "received garbled data: " <> B8.unpack (B8.fromStrict rawMsg))
|
||||||
(\jsonMsg -> do
|
(\jsonMsg -> do
|
||||||
STM.atomically $ STM.writeTQueue queue jsonMsg
|
STM.atomically $ STM.writeTQueue queue jsonMsg
|
||||||
)
|
)
|
||||||
|
@ -147,8 +153,8 @@ terminateGameOnSigint stopper = do
|
||||||
void $ liftIO $ installHandler
|
void $ liftIO $ installHandler
|
||||||
keyboardSignal
|
keyboardSignal
|
||||||
(CatchOnce $ do
|
(CatchOnce $ do
|
||||||
putStrLn "receiveMessage: SIGINT caught, terminating…"
|
logPrintIO (rcLogLevel rc) Info "receiveMessage: SIGINT caught, terminating…"
|
||||||
liftIO $ partingMessage (rcClientUUID rc) (rcSocket rc)
|
liftIO $ partingMessage (rcLogLevel rc) (rcClientUUID rc) (rcSocket rc)
|
||||||
atomicWriteIORef stopper True
|
atomicWriteIORef stopper True
|
||||||
-- Vty.shutdown (clientVty currentState)
|
-- Vty.shutdown (clientVty currentState)
|
||||||
-- Raise SIGINT again so it does not get blocked
|
-- Raise SIGINT again so it does not get blocked
|
||||||
|
@ -157,10 +163,11 @@ terminateGameOnSigint stopper = do
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
partingMessage
|
partingMessage
|
||||||
:: UUID
|
:: LogLevel
|
||||||
|
-> UUID
|
||||||
-> Socket
|
-> Socket
|
||||||
-> IO ()
|
-> IO ()
|
||||||
partingMessage clientId sock = do
|
partingMessage curLevel clientId sock = do
|
||||||
sendMessage (ClientMessage clientId ClientQuit) sock
|
sendMessage curLevel (ClientMessage clientId ClientQuit) sock
|
||||||
putStrLn "sent parting message to server"
|
putStrLn "sent parting message to server"
|
||||||
threadDelay (10 ^ 6)
|
threadDelay (10 ^ (6 :: Int))
|
||||||
|
|
|
@ -18,7 +18,8 @@ handleEvent
|
||||||
-> Game ()
|
-> Game ()
|
||||||
handleEvent (EvKey KEsc _) = do
|
handleEvent (EvKey KEsc _) = do
|
||||||
st <- get
|
st <- get
|
||||||
liftIO $ gracefulExit st "Quitting due to user input"
|
curLevel <- asks rcLogLevel
|
||||||
|
liftIO $ gracefulExit curLevel st "Quitting due to user input"
|
||||||
handleEvent _ =
|
handleEvent _ =
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import Data.IORef (readIORef)
|
||||||
import Client.Communication
|
import Client.Communication
|
||||||
import Client.Events
|
import Client.Events
|
||||||
import Client.Graphics
|
import Client.Graphics
|
||||||
|
import Client.Log (logPrint)
|
||||||
import Client.Types
|
import Client.Types
|
||||||
|
|
||||||
import Library.Types
|
import Library.Types
|
||||||
|
@ -25,14 +26,15 @@ runGame = do
|
||||||
sock <- asks rcSocket
|
sock <- asks rcSocket
|
||||||
messageQueue <- asks rcMessageQueue
|
messageQueue <- asks rcMessageQueue
|
||||||
clientId <- asks rcClientUUID
|
clientId <- asks rcClientUUID
|
||||||
|
curLevel <- asks rcLogLevel
|
||||||
st <- get
|
st <- get
|
||||||
vty <- clientVty <$> liftIO (atomically $ readTMVar (scClientState st))
|
vty <- clientVty <$> liftIO (atomically $ readTMVar (scClientState st))
|
||||||
liftIO $ sendMessage (ClientMessage clientId ClientReady) sock
|
liftIO $ sendMessage curLevel (ClientMessage clientId ClientReady) sock
|
||||||
liftIO $ putStrLn "entering game loop"
|
logPrint Info "entering game loop"
|
||||||
whileM_
|
whileM_
|
||||||
(liftIO (not <$> readIORef (scStopper st)))
|
(liftIO (not <$> readIORef (scStopper st)))
|
||||||
(do
|
(do
|
||||||
liftIO $ receiveMessage sock messageQueue st
|
liftIO $ receiveMessage curLevel sock messageQueue st
|
||||||
-- liftIO $ putStrLn "received messages"
|
-- liftIO $ putStrLn "received messages"
|
||||||
handleMessages
|
handleMessages
|
||||||
-- liftIO $ putStrLn "handled messages"
|
-- liftIO $ putStrLn "handled messages"
|
||||||
|
@ -43,7 +45,7 @@ runGame = do
|
||||||
draw
|
draw
|
||||||
-- liftIO $ putStrLn "drew"
|
-- liftIO $ putStrLn "drew"
|
||||||
)
|
)
|
||||||
|
logPrint Info "left game loop"
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
liftIO $ putStrLn "left game loop"
|
partingMessage curLevel clientId sock
|
||||||
partingMessage clientId sock
|
threadDelay (10 ^ (6 :: Int))
|
||||||
threadDelay (10 ^ 6)
|
|
||||||
|
|
|
@ -14,8 +14,6 @@ import Graphics.Vty
|
||||||
|
|
||||||
import Linear hiding (trace)
|
import Linear hiding (trace)
|
||||||
|
|
||||||
import Debug.Trace
|
|
||||||
|
|
||||||
-- internal imports
|
-- internal imports
|
||||||
|
|
||||||
import Client.Types
|
import Client.Types
|
||||||
|
|
29
src-client/Client/Log.hs
Normal file
29
src-client/Client/Log.hs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module Client.Log where
|
||||||
|
|
||||||
|
import Control.Monad (when)
|
||||||
|
|
||||||
|
import Control.Monad.RWS (asks, MonadIO (liftIO))
|
||||||
|
|
||||||
|
import Debug.Trace (traceIO)
|
||||||
|
|
||||||
|
-- internal imports
|
||||||
|
|
||||||
|
import Client.Types (Game, LogLevel, ReaderContainer(rcLogLevel))
|
||||||
|
|
||||||
|
logPrint
|
||||||
|
:: LogLevel
|
||||||
|
-> String
|
||||||
|
-> Game ()
|
||||||
|
logPrint level msg = do
|
||||||
|
curLevel <- asks rcLogLevel
|
||||||
|
when (level <= curLevel) $
|
||||||
|
liftIO $ traceIO $ show level <> ": " <> msg
|
||||||
|
|
||||||
|
logPrintIO
|
||||||
|
:: LogLevel
|
||||||
|
-> LogLevel
|
||||||
|
-> String
|
||||||
|
-> IO ()
|
||||||
|
logPrintIO curLevel level msg = do
|
||||||
|
when (level <= curLevel) $
|
||||||
|
liftIO $ traceIO $ show level <> ": " <> msg
|
|
@ -21,22 +21,39 @@ import Options.Applicative as O
|
||||||
|
|
||||||
import Library.Types
|
import Library.Types
|
||||||
|
|
||||||
newtype Options = Options
|
data Options = Options
|
||||||
{ optSockLoc :: FilePath
|
{ optLogLevel :: LogLevel
|
||||||
|
, optSockLoc :: FilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
options :: O.Parser Options
|
options :: O.Parser Options
|
||||||
options = Options
|
options = Options
|
||||||
<$> argument str
|
<$> option auto
|
||||||
|
( metavar "LEVEL"
|
||||||
|
<> help "Log level (Error, Warning, Info or Verbose)"
|
||||||
|
<> long "log"
|
||||||
|
<> short 'l'
|
||||||
|
<> value Warning
|
||||||
|
)
|
||||||
|
<*> argument str
|
||||||
( metavar "FILEPATH"
|
( metavar "FILEPATH"
|
||||||
<> help "Location of the server's socket"
|
<> help "Location of the server's socket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data LogLevel
|
||||||
|
= Error
|
||||||
|
| Warning
|
||||||
|
| Info
|
||||||
|
| Verbose
|
||||||
|
deriving (Eq, Ord, Show, Read)
|
||||||
|
|
||||||
data ReaderContainer = ReaderContainer
|
data ReaderContainer = ReaderContainer
|
||||||
{ rcSocket :: Socket
|
{ rcSocket :: Socket
|
||||||
, rcClientUUID :: UUID
|
, rcClientUUID :: UUID
|
||||||
, rcMessageQueue :: STM.TQueue ServerMessage
|
, rcMessageQueue :: STM.TQueue ServerMessage
|
||||||
, rcEventQueue :: STM.TQueue Event
|
, rcEventQueue :: STM.TQueue Event
|
||||||
|
, rcLogLevel :: LogLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
data StateContainer = StateContainer
|
data StateContainer = StateContainer
|
||||||
|
|
|
@ -23,6 +23,7 @@ import Options.Applicative
|
||||||
|
|
||||||
import Client.Communication
|
import Client.Communication
|
||||||
import Client.Game
|
import Client.Game
|
||||||
|
import Client.Log
|
||||||
import Client.Types
|
import Client.Types
|
||||||
|
|
||||||
import Library.Types
|
import Library.Types
|
||||||
|
@ -30,10 +31,10 @@ import Library.Types
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
putStrLn "Hello, Arena!"
|
putStrLn "Hello, Arena!"
|
||||||
(Options socketLocation) <- execParser opts
|
(Options logLevel socketLocation) <- execParser opts
|
||||||
putStrLn $ "connecting to Socket " <> socketLocation
|
logPrintIO Info logLevel $ "connecting to Socket " <> socketLocation
|
||||||
sock <- connectSocket socketLocation
|
sock <- connectSocket socketLocation
|
||||||
putStrLn "connected"
|
logPrintIO Info logLevel "connected"
|
||||||
|
|
||||||
|
|
||||||
messageQueue <- STM.newTQueueIO
|
messageQueue <- STM.newTQueueIO
|
||||||
|
@ -41,24 +42,31 @@ main = do
|
||||||
|
|
||||||
|
|
||||||
-- threadDelay $ 1 * 10 ^ 6
|
-- threadDelay $ 1 * 10 ^ 6
|
||||||
mockClientState <- STM.newTMVarIO (ClientState undefined False)
|
mockClientState <- STM.newTMVarIO
|
||||||
let mockState = StateContainer undefined mockClientState undefined undefined
|
( ClientState
|
||||||
sendMessage IdRequest sock
|
(error "trying to access vty of mock client state")
|
||||||
awaitResponse sock messageQueue mockState 0
|
False
|
||||||
clientIdMsg <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
)
|
||||||
|
let mockState =
|
||||||
|
StateContainer
|
||||||
|
(error "trying to access wizard of mock game state")
|
||||||
|
mockClientState
|
||||||
|
(error "trying to access map slice of mock game state")
|
||||||
|
(error "trying to access stop condition of mock game state")
|
||||||
|
sendMessage logLevel IdRequest sock
|
||||||
|
clientIdMsg <- awaitResponse logLevel sock messageQueue mockState 0
|
||||||
let clientId = acClientUUID clientIdMsg
|
let clientId = acClientUUID clientIdMsg
|
||||||
putStrLn $ "received client UUID: " <> show clientId
|
logPrintIO Info logLevel $ "received client UUID: " <> show clientId
|
||||||
|
|
||||||
|
|
||||||
putStrLn welcomeText
|
putStrLn welcomeText
|
||||||
threadDelay $ 5 * 10 ^ 6
|
threadDelay $ 5 * 10 ^ (6 :: Int)
|
||||||
|
|
||||||
|
|
||||||
sendMessage (ClientMessage clientId ClientRequestWizard) sock
|
sendMessage logLevel (ClientMessage clientId ClientRequestWizard) sock
|
||||||
-- threadDelay $ 1 * 10 ^ 6
|
-- threadDelay $ 1 * 10 ^ 6
|
||||||
awaitResponse sock messageQueue mockState 1
|
playerWizard <- awaitResponse logLevel sock messageQueue mockState 1
|
||||||
playerWizard <- head <$> STM.atomically (STM.flushTQueue messageQueue)
|
logPrintIO Info logLevel $ "received wizard: " <> show (initWizard playerWizard)
|
||||||
putStrLn $ "received wizard: " <> show (initWizard playerWizard)
|
|
||||||
|
|
||||||
vty <- mkVty defaultConfig
|
vty <- mkVty defaultConfig
|
||||||
hideCursor (outputIface vty)
|
hideCursor (outputIface vty)
|
||||||
|
@ -69,24 +77,24 @@ main = do
|
||||||
stopper <- newIORef False
|
stopper <- newIORef False
|
||||||
clientState <- STM.newTMVarIO (ClientState vty False)
|
clientState <- STM.newTMVarIO (ClientState vty False)
|
||||||
let initSlice = MapSlice (M.matrix 9 9 (const Nothing)) []
|
let initSlice = MapSlice (M.matrix 9 9 (const Nothing)) []
|
||||||
let initRead = ReaderContainer sock clientId messageQueue eventQueue
|
let initRead = ReaderContainer sock clientId messageQueue eventQueue logLevel
|
||||||
initState = StateContainer (initWizard playerWizard) clientState initSlice stopper
|
initState = StateContainer (initWizard playerWizard) clientState initSlice stopper
|
||||||
-- putStrLn "sending quit message"
|
-- putStrLn "sending quit message"
|
||||||
-- sendMessage (ClientMessage clientId ClientQuit) sock
|
-- sendMessage (ClientMessage clientId ClientQuit) sock
|
||||||
putStrLn "entering Game Monad"
|
logPrintIO Info logLevel "entering Game Monad"
|
||||||
void $ execRWST
|
void $ execRWST
|
||||||
(do
|
(do
|
||||||
terminateGameOnSigint stopper
|
terminateGameOnSigint stopper
|
||||||
liftIO $ putStrLn "entering main game function"
|
logPrint Verbose "entering main game function"
|
||||||
runGame
|
runGame
|
||||||
)
|
)
|
||||||
initRead
|
initRead
|
||||||
initState
|
initState
|
||||||
putStrLn "Shutting down client…"
|
logPrintIO Info logLevel "Shutting down client…"
|
||||||
showCursor (outputIface vty)
|
showCursor (outputIface vty)
|
||||||
shutdown vty
|
shutdown vty
|
||||||
threadDelay (10 ^ 3)
|
threadDelay 1000
|
||||||
putStrLn "Closing connection to server…"
|
logPrintIO Info logLevel "Closing connection to server…"
|
||||||
close sock
|
close sock
|
||||||
putStrLn "bye bye"
|
putStrLn "bye bye"
|
||||||
where
|
where
|
||||||
|
@ -112,17 +120,18 @@ welcomeText = mconcat $ map (<> "\n")
|
||||||
]
|
]
|
||||||
|
|
||||||
awaitResponse
|
awaitResponse
|
||||||
:: Socket
|
:: LogLevel
|
||||||
|
-> Socket
|
||||||
-> STM.TQueue ServerMessage
|
-> STM.TQueue ServerMessage
|
||||||
-> StateContainer
|
-> StateContainer
|
||||||
-> Int
|
-> Int
|
||||||
-> IO ()
|
-> IO ServerMessage
|
||||||
awaitResponsea _ _ _ 10 = error "Tries to communicate with server exceeded"
|
awaitResponse _ _ _ _ 10 = error "Tries to communicate with server exceeded"
|
||||||
awaitResponse sock messageQueue mockState numTries = do
|
awaitResponse curLevel sock messageQueue mockState numTries = do
|
||||||
receiveMessage sock messageQueue mockState
|
receiveMessage curLevel sock messageQueue mockState
|
||||||
responsePresent <- not <$> STM.atomically (STM.isEmptyTQueue messageQueue)
|
responsePresent <- not <$> STM.atomically (STM.isEmptyTQueue messageQueue)
|
||||||
if responsePresent
|
if responsePresent
|
||||||
then pure ()
|
then STM.atomically $ STM.readTQueue messageQueue
|
||||||
else do
|
else do
|
||||||
threadDelay $ 10 ^ 6
|
threadDelay $ 10 ^ (6 :: Int)
|
||||||
awaitResponse sock messageQueue mockState (succ numTries)
|
awaitResponse curLevel sock messageQueue mockState (numTries + 1)
|
||||||
|
|
|
@ -40,6 +40,7 @@ executable wizard-wipeout-client
|
||||||
Client.Events
|
Client.Events
|
||||||
Client.Game
|
Client.Game
|
||||||
Client.Graphics
|
Client.Graphics
|
||||||
|
Client.Log
|
||||||
Client.Types
|
Client.Types
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base >=4.17.2.1
|
build-depends: base >=4.17.2.1
|
||||||
|
|
Loading…
Reference in a new issue