{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Implementation.MonadClientImplementation
( executorCli
#ifdef EXPOSE_INTERNAL
, CliState(..), CliImplementation(..)
#endif
) where
import Prelude ()
import Game.LambdaHack.Core.Prelude
import Control.Concurrent
import qualified Control.Monad.IO.Class as IO
import Control.Monad.Trans.State.Strict hiding (State)
import Game.LambdaHack.Atomic (MonadStateWrite (..))
import Game.LambdaHack.Client
import qualified Game.LambdaHack.Client.BfsM as BfsM
import Game.LambdaHack.Client.HandleAtomicM
import Game.LambdaHack.Client.HandleResponseM
import Game.LambdaHack.Client.LoopM
import Game.LambdaHack.Client.MonadClient
import Game.LambdaHack.Client.State
import Game.LambdaHack.Client.UI
import Game.LambdaHack.Client.UI.SessionUI
import Game.LambdaHack.Common.Kind
import Game.LambdaHack.Common.Types
import Game.LambdaHack.Common.MonadStateRead
import qualified Game.LambdaHack.Common.Save as Save
import Game.LambdaHack.Common.State
import Game.LambdaHack.Server (ChanServer (..))
data CliState = CliState
{ cliState :: State
, cliClient :: StateClient
, cliSession :: Maybe SessionUI
, cliDict :: ChanServer
, cliToSave :: Save.ChanSave (StateClient, Maybe SessionUI)
}
newtype CliImplementation a = CliImplementation
{ runCliImplementation :: StateT CliState IO a }
deriving (Monad, Functor, Applicative)
instance MonadStateRead CliImplementation where
{-# INLINE getsState #-}
getsState f = CliImplementation $ gets $ f . cliState
instance MonadStateWrite CliImplementation where
{-# INLINE modifyState #-}
modifyState f = CliImplementation $ state $ \cliS ->
let !newCliS = cliS {cliState = f $ cliState cliS}
in ((), newCliS)
{-# INLINE putState #-}
putState newCliState = CliImplementation $ state $ \cliS ->
let !newCliS = cliS {cliState = newCliState}
in ((), newCliS)
instance MonadClientRead CliImplementation where
{-# INLINE getsClient #-}
getsClient f = CliImplementation $ gets $ f . cliClient
liftIO = CliImplementation . IO.liftIO
instance MonadClient CliImplementation where
{-# INLINE modifyClient #-}
modifyClient f = CliImplementation $ state $ \cliS ->
let !newCliS = cliS {cliClient = f $ cliClient cliS}
in ((), newCliS)
instance MonadClientSetup CliImplementation where
saveClient = CliImplementation $ do
toSave <- gets cliToSave
cli <- gets cliClient
msess <- gets cliSession
IO.liftIO $ Save.saveToChan toSave (cli, msess)
restartClient = CliImplementation $ state $ \cliS ->
case cliSession cliS of
Just sess ->
let !newSess = (emptySessionUI (sUIOptions sess))
{ schanF = schanF sess
, sccui = sccui sess
, shistory = shistory sess
, sstart = sstart sess
, sgstart = sgstart sess
, sallTime = sallTime sess
, snframes = snframes sess
, sallNframes = sallNframes sess
}
!newCliS = cliS {cliSession = Just newSess}
in ((), newCliS)
Nothing -> ((), cliS)
instance MonadClientUI CliImplementation where
{-# INLINE getsSession #-}
getsSession f = CliImplementation $ gets $ f . fromJust . cliSession
{-# INLINE modifySession #-}
modifySession f = CliImplementation $ state $ \cliS ->
let !newCliSession = f $ fromJust $ cliSession cliS
!newCliS = cliS {cliSession = Just newCliSession}
in ((), newCliS)
updateClientLeader aid = do
s <- getState
modifyClient $ updateLeader aid s
getCacheBfs = BfsM.getCacheBfs
getCachePath = BfsM.getCachePath
instance MonadClientReadResponse CliImplementation where
receiveResponse = CliImplementation $ do
ChanServer{responseS} <- gets cliDict
IO.liftIO $ takeMVar responseS
instance MonadClientWriteRequest CliImplementation where
sendRequestAI scmd = CliImplementation $ do
ChanServer{requestAIS} <- gets cliDict
IO.liftIO $ putMVar requestAIS scmd
sendRequestUI scmd = CliImplementation $ do
ChanServer{requestUIS} <- gets cliDict
IO.liftIO $ putMVar (fromJust requestUIS) scmd
clientHasUI = CliImplementation $ do
mSession <- gets cliSession
return $! isJust mSession
instance MonadClientAtomic CliImplementation where
{-# INLINE execUpdAtomic #-}
execUpdAtomic _ = return ()
{-# INLINE execPutState #-}
execPutState = putState
executorCli :: CCUI -> UIOptions -> ClientOptions
-> COps
-> Bool
-> FactionId
-> ChanServer
-> IO ()
executorCli ccui sUIOptions clientOptions cops isUI fid cliDict =
let cliSession | isUI = Just $ emptySessionUI sUIOptions
| otherwise = Nothing
stateToFileName (cli, _) =
ssavePrefixCli (soptions cli) <> Save.saveNameCli cops (sside cli)
totalState cliToSave = CliState
{ cliState = updateCOpsAndCachedData (const cops) emptyState
, cliClient = emptyStateClient fid
, cliDict
, cliToSave
, cliSession
}
m = loopCli ccui sUIOptions clientOptions
exe = evalStateT (runCliImplementation m) . totalState
in Save.wrapInSaves cops stateToFileName exe