make client logging sensible

This commit is contained in:
nek0 2024-11-07 17:47:11 +01:00
parent 892333bb6e
commit bbf60abc59
8 changed files with 125 additions and 61 deletions

View file

@ -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))

View file

@ -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 ()

View file

@ -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)

View file

@ -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
View 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

View file

@ -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

View file

@ -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)

View file

@ -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