    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    -- | The implementation of our custom game client monads. Just as any other
    -- component of the library, this implementation can be substituted.
    module Implementation.MonadClientImplementation
      ( executorCli
    #ifdef EXPOSE_INTERNAL
        -- * Internal operations
      , 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.Common.ClientOptions
    import           Game.LambdaHack.Common.Kind
    import           Game.LambdaHack.Common.MonadStateRead
    import qualified Game.LambdaHack.Common.Save as Save
    import           Game.LambdaHack.Common.State
    import           Game.LambdaHack.Common.Types
    import           Game.LambdaHack.Server (ChanServer (..))

    data CliState = CliState
      { CliState -> State
    cliState   :: State            -- ^ current global state
      , CliState -> StateClient
    cliClient  :: StateClient      -- ^ current client state
      , CliState -> Maybe SessionUI
    cliSession :: Maybe SessionUI  -- ^ UI state, empty for AI clients
      , CliState -> ChanServer
    cliDict    :: ChanServer       -- ^ this client connection information
      , CliState -> ChanSave (StateClient, Maybe SessionUI)
    cliToSave  :: Save.ChanSave (StateClient, Maybe SessionUI)
                                       -- ^ connection to the save thread
      }

    -- | Client state transformation monad.
    newtype CliImplementation a = CliImplementation
      { forall a. CliImplementation a -> StateT CliState IO a
    runCliImplementation :: StateT CliState IO a }
      deriving (Applicative CliImplementation
    Applicative CliImplementation =>
    (forall a b.
     CliImplementation a
     -> (a -> CliImplementation b) -> CliImplementation b)
    -> (forall a b.
        CliImplementation a -> CliImplementation b -> CliImplementation b)
    -> (forall a. a -> CliImplementation a)
    -> Monad CliImplementation
    forall a. a -> CliImplementation a
    forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    forall a b.
    CliImplementation a
    -> (a -> CliImplementation b) -> CliImplementation b
    forall (m :: * -> *).
    Applicative m =>
    (forall a b. m a -> (a -> m b) -> m b)
    -> (forall a b. m a -> m b -> m b)
    -> (forall a. a -> m a)
    -> Monad m
    $c>>= :: forall a b.
    CliImplementation a
    -> (a -> CliImplementation b) -> CliImplementation b
    >>= :: forall a b.
    CliImplementation a
    -> (a -> CliImplementation b) -> CliImplementation b
    $c>> :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    >> :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    $creturn :: forall a. a -> CliImplementation a
    return :: forall a. a -> CliImplementation a
    Monad, (forall a b.
     (a -> b) -> CliImplementation a -> CliImplementation b)
    -> (forall a b. a -> CliImplementation b -> CliImplementation a)
    -> Functor CliImplementation
    forall a b. a -> CliImplementation b -> CliImplementation a
    forall a b. (a -> b) -> CliImplementation a -> CliImplementation b
    forall (f :: * -> *).
    (forall a b. (a -> b) -> f a -> f b)
    -> (forall a b. a -> f b -> f a) -> Functor f
    $cfmap :: forall a b. (a -> b) -> CliImplementation a -> CliImplementation b
    fmap :: forall a b. (a -> b) -> CliImplementation a -> CliImplementation b
    $c<$ :: forall a b. a -> CliImplementation b -> CliImplementation a
    <$ :: forall a b. a -> CliImplementation b -> CliImplementation a
    Functor, Functor CliImplementation
    Functor CliImplementation =>
    (forall a. a -> CliImplementation a)
    -> (forall a b.
        CliImplementation (a -> b)
        -> CliImplementation a -> CliImplementation b)
    -> (forall a b c.
        (a -> b -> c)
        -> CliImplementation a
        -> CliImplementation b
        -> CliImplementation c)
    -> (forall a b.
        CliImplementation a -> CliImplementation b -> CliImplementation b)
    -> (forall a b.
        CliImplementation a -> CliImplementation b -> CliImplementation a)
    -> Applicative CliImplementation
    forall a. a -> CliImplementation a
    forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation a
    forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    forall a b.
    CliImplementation (a -> b)
    -> CliImplementation a -> CliImplementation b
    forall a b c.
    (a -> b -> c)
    -> CliImplementation a
    -> CliImplementation b
    -> CliImplementation c
    forall (f :: * -> *).
    Functor f =>
    (forall a. a -> f a)
    -> (forall a b. f (a -> b) -> f a -> f b)
    -> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
    -> (forall a b. f a -> f b -> f b)
    -> (forall a b. f a -> f b -> f a)
    -> Applicative f
    $cpure :: forall a. a -> CliImplementation a
    pure :: forall a. a -> CliImplementation a
    $c<*> :: forall a b.
    CliImplementation (a -> b)
    -> CliImplementation a -> CliImplementation b
    <*> :: forall a b.
    CliImplementation (a -> b)
    -> CliImplementation a -> CliImplementation b
    $cliftA2 :: forall a b c.
    (a -> b -> c)
    -> CliImplementation a
    -> CliImplementation b
    -> CliImplementation c
    liftA2 :: forall a b c.
    (a -> b -> c)
    -> CliImplementation a
    -> CliImplementation b
    -> CliImplementation c
    $c*> :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    *> :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation b
    $c<* :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation a
    <* :: forall a b.
    CliImplementation a -> CliImplementation b -> CliImplementation a
    Applicative)

    instance MonadStateRead CliImplementation where
      {-# INLINE getsState #-}
      getsState :: forall a. (State -> a) -> CliImplementation a
    getsState State -> a
    f = StateT CliState IO a -> CliImplementation a
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO a -> CliImplementation a)
    -> StateT CliState IO a -> CliImplementation a
    forall a b. (a -> b) -> a -> b
    $ (CliState -> a) -> StateT CliState IO a
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets ((CliState -> a) -> StateT CliState IO a)
    -> (CliState -> a) -> StateT CliState IO a
    forall a b. (a -> b) -> a -> b
    $ State -> a
    f (State -> a) -> (CliState -> State) -> CliState -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . CliState -> State
    cliState

    instance MonadStateWrite CliImplementation where
      {-# INLINE modifyState #-}
      modifyState :: (State -> State) -> CliImplementation ()
    modifyState State -> State
    f = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
    state ((CliState -> ((), CliState)) -> StateT CliState IO ())
    -> (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall a b. (a -> b) -> a -> b
    $ \CliState
    cliS ->
        let !newCliS :: CliState
    newCliS = CliState
    cliS {cliState = f $ cliState cliS}
        in ((), CliState
    newCliS)
      {-# INLINE putState #-}
      putState :: State -> CliImplementation ()
    putState State
    newCliState = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
    state ((CliState -> ((), CliState)) -> StateT CliState IO ())
    -> (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall a b. (a -> b) -> a -> b
    $ \CliState
    cliS ->
        let !newCliS :: CliState
    newCliS = CliState
    cliS {cliState = newCliState}
        in ((), CliState
    newCliS)

    instance MonadClientRead CliImplementation where
      {-# INLINE getsClient #-}
      getsClient :: forall a. (StateClient -> a) -> CliImplementation a
    getsClient StateClient -> a
    f = StateT CliState IO a -> CliImplementation a
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO a -> CliImplementation a)
    -> StateT CliState IO a -> CliImplementation a
    forall a b. (a -> b) -> a -> b
    $ (CliState -> a) -> StateT CliState IO a
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets ((CliState -> a) -> StateT CliState IO a)
    -> (CliState -> a) -> StateT CliState IO a
    forall a b. (a -> b) -> a -> b
    $ StateClient -> a
    f (StateClient -> a) -> (CliState -> StateClient) -> CliState -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . CliState -> StateClient
    cliClient
      liftIO :: forall a. IO a -> CliImplementation a
    liftIO = StateT CliState IO a -> CliImplementation a
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO a -> CliImplementation a)
    -> (IO a -> StateT CliState IO a) -> IO a -> CliImplementation a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO a -> StateT CliState IO a
    forall a. IO a -> StateT CliState IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    IO.liftIO

    instance MonadClient CliImplementation where
      {-# INLINE modifyClient #-}
      modifyClient :: (StateClient -> StateClient) -> CliImplementation ()
    modifyClient StateClient -> StateClient
    f = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
    state ((CliState -> ((), CliState)) -> StateT CliState IO ())
    -> (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall a b. (a -> b) -> a -> b
    $ \CliState
    cliS ->
        let !newCliS :: CliState
    newCliS = CliState
    cliS {cliClient = f $ cliClient cliS}
        in ((), CliState
    newCliS)

    instance MonadClientSetup CliImplementation where
      saveClient :: CliImplementation ()
    saveClient = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ do
        toSave <- (CliState -> ChanSave (StateClient, Maybe SessionUI))
    -> StateT CliState IO (ChanSave (StateClient, Maybe SessionUI))
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets CliState -> ChanSave (StateClient, Maybe SessionUI)
    cliToSave
        cli <- gets cliClient
        msess <- gets cliSession
        IO.liftIO $ Save.saveToChan toSave (cli, msess)

    instance MonadClientUI CliImplementation where
      {-# INLINE getsSession #-}
      getsSession :: forall a. (SessionUI -> a) -> CliImplementation a
    getsSession SessionUI -> a
    f = StateT CliState IO a -> CliImplementation a
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO a -> CliImplementation a)
    -> StateT CliState IO a -> CliImplementation a
    forall a b. (a -> b) -> a -> b
    $ (CliState -> a) -> StateT CliState IO a
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets ((CliState -> a) -> StateT CliState IO a)
    -> (CliState -> a) -> StateT CliState IO a
    forall a b. (a -> b) -> a -> b
    $ SessionUI -> a
    f (SessionUI -> a) -> (CliState -> SessionUI) -> CliState -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe SessionUI -> SessionUI
    forall a. HasCallStack => Maybe a -> a
    fromJust (Maybe SessionUI -> SessionUI)
    -> (CliState -> Maybe SessionUI) -> CliState -> SessionUI
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . CliState -> Maybe SessionUI
    cliSession
      {-# INLINE modifySession #-}
      modifySession :: (SessionUI -> SessionUI) -> CliImplementation ()
    modifySession SessionUI -> SessionUI
    f = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall (m :: * -> *) s a. Monad m => (s -> (a, s)) -> StateT s m a
    state ((CliState -> ((), CliState)) -> StateT CliState IO ())
    -> (CliState -> ((), CliState)) -> StateT CliState IO ()
    forall a b. (a -> b) -> a -> b
    $ \CliState
    cliS ->
        let !newCliSession :: SessionUI
    newCliSession = SessionUI -> SessionUI
    f (SessionUI -> SessionUI) -> SessionUI -> SessionUI
    forall a b. (a -> b) -> a -> b
    $ Maybe SessionUI -> SessionUI
    forall a. HasCallStack => Maybe a -> a
    fromJust (Maybe SessionUI -> SessionUI) -> Maybe SessionUI -> SessionUI
    forall a b. (a -> b) -> a -> b
    $ CliState -> Maybe SessionUI
    cliSession CliState
    cliS
            !newCliS :: CliState
    newCliS = CliState
    cliS {cliSession = Just newCliSession}
        in ((), CliState
    newCliS)
      updateClientLeader :: ActorId -> CliImplementation ()
    updateClientLeader ActorId
    aid = do
        s <- CliImplementation State
    forall (m :: * -> *). MonadStateRead m => m State
    getState
        modifyClient $ updateLeader aid s
      getCacheBfs :: ActorId -> CliImplementation (Array BfsDistance)
    getCacheBfs = ActorId -> CliImplementation (Array BfsDistance)
    forall (m :: * -> *).
    MonadClient m =>
    ActorId -> m (Array BfsDistance)
    BfsM.getCacheBfs
      getCachePath :: ActorId -> Point -> CliImplementation (Maybe AndPath)
    getCachePath = ActorId -> Point -> CliImplementation (Maybe AndPath)
    forall (m :: * -> *).
    MonadClient m =>
    ActorId -> Point -> m (Maybe AndPath)
    BfsM.getCachePath

    instance MonadClientReadResponse CliImplementation where
      receiveResponse :: CliImplementation Response
    receiveResponse = StateT CliState IO Response -> CliImplementation Response
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO Response -> CliImplementation Response)
    -> StateT CliState IO Response -> CliImplementation Response
    forall a b. (a -> b) -> a -> b
    $ do
        ChanServer{responseS} <- (CliState -> ChanServer) -> StateT CliState IO ChanServer
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets CliState -> ChanServer
    cliDict
        IO.liftIO $ takeMVar responseS

    instance MonadClientWriteRequest CliImplementation where
      sendRequestAI :: RequestAI -> CliImplementation ()
    sendRequestAI RequestAI
    scmd = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ do
        ChanServer{requestAIS} <- (CliState -> ChanServer) -> StateT CliState IO ChanServer
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets CliState -> ChanServer
    cliDict
        IO.liftIO $ putMVar requestAIS scmd
      sendRequestUI :: RequestUI -> CliImplementation ()
    sendRequestUI RequestUI
    scmd = StateT CliState IO () -> CliImplementation ()
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO () -> CliImplementation ())
    -> StateT CliState IO () -> CliImplementation ()
    forall a b. (a -> b) -> a -> b
    $ do
        ChanServer{requestUIS} <- (CliState -> ChanServer) -> StateT CliState IO ChanServer
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets CliState -> ChanServer
    cliDict
        IO.liftIO $ putMVar (fromJust requestUIS) scmd
      clientHasUI :: CliImplementation Bool
    clientHasUI = StateT CliState IO Bool -> CliImplementation Bool
    forall a. StateT CliState IO a -> CliImplementation a
    CliImplementation (StateT CliState IO Bool -> CliImplementation Bool)
    -> StateT CliState IO Bool -> CliImplementation Bool
    forall a b. (a -> b) -> a -> b
    $ do
        mSession <- (CliState -> Maybe SessionUI)
    -> StateT CliState IO (Maybe SessionUI)
    forall (m :: * -> *) s a. Monad m => (s -> a) -> StateT s m a
    gets CliState -> Maybe SessionUI
    cliSession
        return $! isJust mSession

    instance MonadClientAtomic CliImplementation where
      {-# INLINE execUpdAtomic #-}
      execUpdAtomic :: UpdAtomic -> CliImplementation ()
    execUpdAtomic UpdAtomic
    _ = () -> CliImplementation ()
    forall a. a -> CliImplementation a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()  -- handleUpdAtomic, until needed, save resources
        -- Don't catch anything; assume exceptions impossible.
      {-# INLINE execPutState #-}
      execPutState :: State -> CliImplementation ()
    execPutState = State -> CliImplementation ()
    forall (m :: * -> *). MonadStateWrite m => State -> m ()
    putState

    -- | Run the main client loop, with the given arguments and empty
    -- initial states, in the @IO@ monad.
    executorCli :: CCUI -> UIOptions -> ClientOptions -> Bool
                -> COps
                -> FactionId
                -> ChanServer
                -> IO ()
    executorCli :: CCUI
    -> UIOptions
    -> ClientOptions
    -> Bool
    -> COps
    -> FactionId
    -> ChanServer
    -> IO ()
    executorCli CCUI
    ccui UIOptions
    sUIOptions ClientOptions
    clientOptions Bool
    startsNewGame
                cops :: COps
    cops@COps{RuleContent
    corule :: RuleContent
    corule :: COps -> RuleContent
    corule} FactionId
    fid ChanServer
    cliDict =
      let cliSession :: Maybe SessionUI
    cliSession | Maybe (CliSerQueue RequestUI) -> Bool
    forall a. Maybe a -> Bool
    isJust (ChanServer -> Maybe (CliSerQueue RequestUI)
    requestUIS ChanServer
    cliDict) =
                         SessionUI -> Maybe SessionUI
    forall a. a -> Maybe a
    Just (SessionUI -> Maybe SessionUI) -> SessionUI -> Maybe SessionUI
    forall a b. (a -> b) -> a -> b
    $ UIOptions -> SessionUI
    emptySessionUI UIOptions
    sUIOptions
                     | Bool
    otherwise = Maybe SessionUI
    forall a. Maybe a
    Nothing
          stateToFileName :: (StateClient, Maybe SessionUI) -> String
    stateToFileName (StateClient
    cli, Maybe SessionUI
    _) =
            ClientOptions -> String
    ssavePrefixCli (StateClient -> ClientOptions
    soptions StateClient
    cli) String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> RuleContent -> FactionId -> String
    Save.saveNameCli RuleContent
    corule (StateClient -> FactionId
    sside StateClient
    cli)
          totalState :: ChanSave (StateClient, Maybe SessionUI) -> CliState
    totalState ChanSave (StateClient, Maybe SessionUI)
    cliToSave = CliState
            { cliState :: State
    cliState = (COps -> COps) -> State -> State
    updateCOpsAndCachedData (COps -> COps -> COps
    forall a b. a -> b -> a
    const COps
    cops) State
    emptyState
                -- state is empty, so the cached data is left empty and untouched
            , cliClient :: StateClient
    cliClient = FactionId -> StateClient
    emptyStateClient FactionId
    fid
            , ChanServer
    cliDict :: ChanServer
    cliDict :: ChanServer
    cliDict
            , ChanSave (StateClient, Maybe SessionUI)
    cliToSave :: ChanSave (StateClient, Maybe SessionUI)
    cliToSave :: ChanSave (StateClient, Maybe SessionUI)
    cliToSave
            , Maybe SessionUI
    cliSession :: Maybe SessionUI
    cliSession :: Maybe SessionUI
    cliSession
            }
          m :: CliImplementation ()
    m = CCUI -> UIOptions -> ClientOptions -> Bool -> CliImplementation ()
    forall (m :: * -> *).
    (MonadClientSetup m, MonadClientUI m, MonadClientAtomic m,
     MonadClientReadResponse m, MonadClientWriteRequest m) =>
    CCUI -> UIOptions -> ClientOptions -> Bool -> m ()
    loopCli CCUI
    ccui UIOptions
    sUIOptions ClientOptions
    clientOptions Bool
    startsNewGame
          exe :: ChanSave (StateClient, Maybe SessionUI) -> IO ()
    exe = StateT CliState IO () -> CliState -> IO ()
    forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
    evalStateT (CliImplementation () -> StateT CliState IO ()
    forall a. CliImplementation a -> StateT CliState IO a
    runCliImplementation CliImplementation ()
    m) (CliState -> IO ())
    -> (ChanSave (StateClient, Maybe SessionUI) -> CliState)
    -> ChanSave (StateClient, Maybe SessionUI)
    -> IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ChanSave (StateClient, Maybe SessionUI) -> CliState
    totalState
      in COps
    -> ((StateClient, Maybe SessionUI) -> String)
    -> (ChanSave (StateClient, Maybe SessionUI) -> IO ())
    -> IO ()
    forall a.
    Binary a =>
    COps -> (a -> String) -> (ChanSave a -> IO ()) -> IO ()
    Save.wrapInSaves COps
    cops (StateClient, Maybe SessionUI) -> String
    stateToFileName ChanSave (StateClient, Maybe SessionUI) -> IO ()
    exe