{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
module Netcode.IO.Client (
    -- * Clients

    -- ** Client-specific callbacks
      ClientStateChangeCallback

    -- ** Client configs
    , ClientConfig
    , defaultClientConfig
    , setClientStateChangeCallback, clearClientStateChangeCallback
    , setClientSendReceiveOverrides, clearClientSendReceiveOverrides

    -- ** Client objects
    , Client
    , createClient
    , destroyClient
    , generateClientID
    , connectClient
    , disconnectClient
    , updateClient
    , sendPacketFromClient
    , receivePacketFromServer
    , nextClientPacketSequence
    , getClientPort
    , withClientServerAddress

    -- ** Client state
    , ClientState(..)
    , getClientState
    , isClientDisconnected

    -- ** Connect Tokens
    , ConnectToken
    , maximumServersPerConnect
    , maximumUserDataSize
    , privateKeySize
    , generateConnectToken
) where

--------------------------------------------------------------------------------

import Control.Monad         (when)
import Data.Data             (Data)
import Data.Typeable         (Typeable)
import Data.Word             (Word8, Word16, Word64)
import Foreign.C.String      (withCString)
import Foreign.C.Types       (CDouble(..), CInt)
import Foreign.Concurrent    (newForeignPtr)
import Foreign.ForeignPtr    ( ForeignPtr, newForeignPtr_, mallocForeignPtrBytes
                             , withForeignPtr
                             )
import Foreign.Marshal.Alloc (alloca)
import Foreign.Marshal.Array (allocaArray, pokeArray)
import Foreign.Ptr           ( Ptr, nullPtr, castPtr
                             , FunPtr, nullFunPtr, freeHaskellFunPtr
                             )
import Foreign.Storable      (peek, poke, sizeOf, pokeElemOff)
import GHC.Generics          (Generic)

import Bindings.Netcode.IO
import Netcode.IO.Address
import Netcode.IO.Callbacks
import Netcode.IO.Packet

--------------------------------------------------------------------------------

freeNullFunPtr :: FunPtr a -> IO ()
freeNullFunPtr :: FunPtr a -> IO ()
freeNullFunPtr FunPtr a
x
    | FunPtr a
x FunPtr a -> FunPtr a -> Bool
forall a. Eq a => a -> a -> Bool
== FunPtr a
forall a. FunPtr a
nullFunPtr = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    | Bool
otherwise       = FunPtr a -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr a
x

--------------------------------------------------------------------------------

-- | The possible connection states of a 'Client'. The default state is
-- 'ClientState'Disconnected'.
data ClientState
    = ClientState'ConnectTokenExpired
    | ClientState'InvalidConnectToken
    | ClientState'ConnectionTimedOut
    | ClientState'ConnectionResponseTimedOut
    | ClientState'ConnectionRequestTimedOut
    | ClientState'ConnectionDenied
    | ClientState'Disconnected
    | ClientState'SendingConnectionRequest
    | ClientState'SendingConnectionResponse
    | ClientState'Connected
    deriving (ClientState -> ClientState -> Bool
(ClientState -> ClientState -> Bool)
-> (ClientState -> ClientState -> Bool) -> Eq ClientState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ClientState -> ClientState -> Bool
$c/= :: ClientState -> ClientState -> Bool
== :: ClientState -> ClientState -> Bool
$c== :: ClientState -> ClientState -> Bool
Eq, Eq ClientState
Eq ClientState
-> (ClientState -> ClientState -> Ordering)
-> (ClientState -> ClientState -> Bool)
-> (ClientState -> ClientState -> Bool)
-> (ClientState -> ClientState -> Bool)
-> (ClientState -> ClientState -> Bool)
-> (ClientState -> ClientState -> ClientState)
-> (ClientState -> ClientState -> ClientState)
-> Ord ClientState
ClientState -> ClientState -> Bool
ClientState -> ClientState -> Ordering
ClientState -> ClientState -> ClientState
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: ClientState -> ClientState -> ClientState
$cmin :: ClientState -> ClientState -> ClientState
max :: ClientState -> ClientState -> ClientState
$cmax :: ClientState -> ClientState -> ClientState
>= :: ClientState -> ClientState -> Bool
$c>= :: ClientState -> ClientState -> Bool
> :: ClientState -> ClientState -> Bool
$c> :: ClientState -> ClientState -> Bool
<= :: ClientState -> ClientState -> Bool
$c<= :: ClientState -> ClientState -> Bool
< :: ClientState -> ClientState -> Bool
$c< :: ClientState -> ClientState -> Bool
compare :: ClientState -> ClientState -> Ordering
$ccompare :: ClientState -> ClientState -> Ordering
$cp1Ord :: Eq ClientState
Ord, Int -> ClientState -> ShowS
[ClientState] -> ShowS
ClientState -> String
(Int -> ClientState -> ShowS)
-> (ClientState -> String)
-> ([ClientState] -> ShowS)
-> Show ClientState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ClientState] -> ShowS
$cshowList :: [ClientState] -> ShowS
show :: ClientState -> String
$cshow :: ClientState -> String
showsPrec :: Int -> ClientState -> ShowS
$cshowsPrec :: Int -> ClientState -> ShowS
Show, Int -> ClientState
ClientState -> Int
ClientState -> [ClientState]
ClientState -> ClientState
ClientState -> ClientState -> [ClientState]
ClientState -> ClientState -> ClientState -> [ClientState]
(ClientState -> ClientState)
-> (ClientState -> ClientState)
-> (Int -> ClientState)
-> (ClientState -> Int)
-> (ClientState -> [ClientState])
-> (ClientState -> ClientState -> [ClientState])
-> (ClientState -> ClientState -> [ClientState])
-> (ClientState -> ClientState -> ClientState -> [ClientState])
-> Enum ClientState
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: ClientState -> ClientState -> ClientState -> [ClientState]
$cenumFromThenTo :: ClientState -> ClientState -> ClientState -> [ClientState]
enumFromTo :: ClientState -> ClientState -> [ClientState]
$cenumFromTo :: ClientState -> ClientState -> [ClientState]
enumFromThen :: ClientState -> ClientState -> [ClientState]
$cenumFromThen :: ClientState -> ClientState -> [ClientState]
enumFrom :: ClientState -> [ClientState]
$cenumFrom :: ClientState -> [ClientState]
fromEnum :: ClientState -> Int
$cfromEnum :: ClientState -> Int
toEnum :: Int -> ClientState
$ctoEnum :: Int -> ClientState
pred :: ClientState -> ClientState
$cpred :: ClientState -> ClientState
succ :: ClientState -> ClientState
$csucc :: ClientState -> ClientState
Enum, ClientState
ClientState -> ClientState -> Bounded ClientState
forall a. a -> a -> Bounded a
maxBound :: ClientState
$cmaxBound :: ClientState
minBound :: ClientState
$cminBound :: ClientState
Bounded, Typeable ClientState
DataType
Constr
Typeable ClientState
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> ClientState -> c ClientState)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c ClientState)
-> (ClientState -> Constr)
-> (ClientState -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c ClientState))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e))
    -> Maybe (c ClientState))
-> ((forall b. Data b => b -> b) -> ClientState -> ClientState)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> ClientState -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> ClientState -> r)
-> (forall u. (forall d. Data d => d -> u) -> ClientState -> [u])
-> (forall u.
    Int -> (forall d. Data d => d -> u) -> ClientState -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> ClientState -> m ClientState)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> ClientState -> m ClientState)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> ClientState -> m ClientState)
-> Data ClientState
ClientState -> DataType
ClientState -> Constr
(forall b. Data b => b -> b) -> ClientState -> ClientState
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> ClientState -> c ClientState
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c ClientState
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> ClientState -> u
forall u. (forall d. Data d => d -> u) -> ClientState -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> ClientState -> m ClientState
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> ClientState -> m ClientState
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c ClientState
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> ClientState -> c ClientState
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c ClientState)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c ClientState)
$cClientState'Connected :: Constr
$cClientState'SendingConnectionResponse :: Constr
$cClientState'SendingConnectionRequest :: Constr
$cClientState'Disconnected :: Constr
$cClientState'ConnectionDenied :: Constr
$cClientState'ConnectionRequestTimedOut :: Constr
$cClientState'ConnectionResponseTimedOut :: Constr
$cClientState'ConnectionTimedOut :: Constr
$cClientState'InvalidConnectToken :: Constr
$cClientState'ConnectTokenExpired :: Constr
$tClientState :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> ClientState -> m ClientState
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> ClientState -> m ClientState
gmapMp :: (forall d. Data d => d -> m d) -> ClientState -> m ClientState
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> ClientState -> m ClientState
gmapM :: (forall d. Data d => d -> m d) -> ClientState -> m ClientState
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> ClientState -> m ClientState
gmapQi :: Int -> (forall d. Data d => d -> u) -> ClientState -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> ClientState -> u
gmapQ :: (forall d. Data d => d -> u) -> ClientState -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> ClientState -> [u]
gmapQr :: (r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
gmapQl :: (r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> ClientState -> r
gmapT :: (forall b. Data b => b -> b) -> ClientState -> ClientState
$cgmapT :: (forall b. Data b => b -> b) -> ClientState -> ClientState
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c ClientState)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c ClientState)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c ClientState)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c ClientState)
dataTypeOf :: ClientState -> DataType
$cdataTypeOf :: ClientState -> DataType
toConstr :: ClientState -> Constr
$ctoConstr :: ClientState -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c ClientState
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c ClientState
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> ClientState -> c ClientState
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> ClientState -> c ClientState
$cp1Data :: Typeable ClientState
Data, Typeable, (forall x. ClientState -> Rep ClientState x)
-> (forall x. Rep ClientState x -> ClientState)
-> Generic ClientState
forall x. Rep ClientState x -> ClientState
forall x. ClientState -> Rep ClientState x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep ClientState x -> ClientState
$cfrom :: forall x. ClientState -> Rep ClientState x
Generic)

_rawClientState :: ClientState -> CInt
_rawClientState :: ClientState -> CInt
_rawClientState ClientState
ClientState'ConnectTokenExpired        = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED
_rawClientState ClientState
ClientState'InvalidConnectToken        = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN
_rawClientState ClientState
ClientState'ConnectionTimedOut         = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT
_rawClientState ClientState
ClientState'ConnectionResponseTimedOut = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMED_OUT
_rawClientState ClientState
ClientState'ConnectionRequestTimedOut  = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT
_rawClientState ClientState
ClientState'ConnectionDenied           = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_DENIED
_rawClientState ClientState
ClientState'Disconnected               = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_DISCONNECTED
_rawClientState ClientState
ClientState'SendingConnectionRequest   = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST
_rawClientState ClientState
ClientState'SendingConnectionResponse  = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE
_rawClientState ClientState
ClientState'Connected                  = CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTED

typedClientState :: CInt -> ClientState
typedClientState :: CInt -> ClientState
typedClientState CInt
raw
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED         = ClientState
ClientState'ConnectTokenExpired
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN         = ClientState
ClientState'InvalidConnectToken
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT          = ClientState
ClientState'ConnectionTimedOut
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMED_OUT = ClientState
ClientState'ConnectionResponseTimedOut
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT  = ClientState
ClientState'ConnectionRequestTimedOut
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTION_DENIED             = ClientState
ClientState'ConnectionDenied
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_DISCONNECTED                  = ClientState
ClientState'Disconnected
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST    = ClientState
ClientState'SendingConnectionRequest
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE   = ClientState
ClientState'SendingConnectionResponse
  | CInt
raw CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_CONNECTED                     = ClientState
ClientState'Connected
  | Bool
otherwise = String -> ClientState
forall a. HasCallStack => String -> a
error String
"Unrecognized client state value"

-- | A client object. This is an opaque type meant to be used in conjunction
-- with this library.
--
-- A 'Client' is generally meant to connect to one of potentially many servers
-- through a 'ConnectToken'. The main loop of the application that manages the
-- lifetime of the client is expected to maintain a running timer with a
-- resolution of at least seconds. This main loop is also expected to call
-- 'updateClient' on a regular basis to allow the library to process incoming
-- packets and send outgoing packets.
data Client = Client 
  { Client -> Ptr C'netcode_client_t
clientPtr :: Ptr C'netcode_client_t
  , Client -> ClientCallbacks
clientCallbacks :: ClientCallbacks
  } deriving (Int -> Client -> ShowS
[Client] -> ShowS
Client -> String
(Int -> Client -> ShowS)
-> (Client -> String) -> ([Client] -> ShowS) -> Show Client
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Client] -> ShowS
$cshowList :: [Client] -> ShowS
show :: Client -> String
$cshow :: Client -> String
showsPrec :: Int -> Client -> ShowS
$cshowsPrec :: Int -> Client -> ShowS
Show)

data ClientCallbacks = ClientCallbacks
  { ClientCallbacks -> C'state_change_callback_t
clientStateChange           :: C'state_change_callback_t
  , ClientCallbacks -> C'send_packet_override_t
clientSendPacketOverride    :: C'send_packet_override_t
  , ClientCallbacks -> C'receive_packet_override_t
clientReceivePacketOverride :: C'receive_packet_override_t
  } deriving (Int -> ClientCallbacks -> ShowS
[ClientCallbacks] -> ShowS
ClientCallbacks -> String
(Int -> ClientCallbacks -> ShowS)
-> (ClientCallbacks -> String)
-> ([ClientCallbacks] -> ShowS)
-> Show ClientCallbacks
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ClientCallbacks] -> ShowS
$cshowList :: [ClientCallbacks] -> ShowS
show :: ClientCallbacks -> String
$cshow :: ClientCallbacks -> String
showsPrec :: Int -> ClientCallbacks -> ShowS
$cshowsPrec :: Int -> ClientCallbacks -> ShowS
Show)

defaultClientCallbacks :: ClientCallbacks
defaultClientCallbacks :: ClientCallbacks
defaultClientCallbacks = C'state_change_callback_t
-> C'send_packet_override_t
-> C'receive_packet_override_t
-> ClientCallbacks
ClientCallbacks C'state_change_callback_t
forall a. FunPtr a
nullFunPtr C'send_packet_override_t
forall a. FunPtr a
nullFunPtr C'receive_packet_override_t
forall a. FunPtr a
nullFunPtr

-- | A 'ClientConfig' is a type that specifies the behavior of a 'Client'.
-- Client configs are pretty spartan: the only options available at this time
-- are setting callbacks.
newtype ClientConfig = ClientConfig
  (   Ptr C'netcode_client_config_t
   -> ClientCallbacks
   -> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
  )

-- | A 'ClientConfig' with no callback overrides.
defaultClientConfig :: ClientConfig
defaultClientConfig :: ClientConfig
defaultClientConfig = (Ptr C'netcode_client_config_t
 -> ClientCallbacks
 -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
ClientConfig ((Ptr C'netcode_client_config_t
  -> ClientCallbacks
  -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
 -> ClientConfig)
-> (Ptr C'netcode_client_config_t
    -> ClientCallbacks
    -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
clientConfig ClientCallbacks
cbs -> do
    Ptr C'netcode_client_config_t -> IO ()
c'netcode_default_client_config Ptr C'netcode_client_config_t
clientConfig
    (Ptr C'netcode_client_config_t, ClientCallbacks)
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_config_t
clientConfig, ClientCallbacks
cbs)

-- | A client-specific callback that gets invoked each time the underlying
-- state of the client changes.
type ClientStateChangeCallback
   = ClientState   -- ^ Old state
  -> ClientState   -- ^ New state
  -> IO ()

mkClientStateChangeCallback :: ClientStateChangeCallback
                            -> IO C'state_change_callback_t
mkClientStateChangeCallback :: ClientStateChangeCallback -> IO C'state_change_callback_t
mkClientStateChangeCallback ClientStateChangeCallback
cb = (Ptr () -> CInt -> CInt -> IO ()) -> IO C'state_change_callback_t
mk'state_change_callback_t ((Ptr () -> CInt -> CInt -> IO ()) -> IO C'state_change_callback_t)
-> (Ptr () -> CInt -> CInt -> IO ())
-> IO C'state_change_callback_t
forall a b. (a -> b) -> a -> b
$ \Ptr ()
_ CInt
oldSt CInt
newSt ->
    ClientStateChangeCallback
cb (CInt -> ClientState
typedClientState CInt
oldSt) (CInt -> ClientState
typedClientState CInt
newSt)

-- | Creates a config that removes the existing 'ClientStateChangeCallback' and
-- instead uses the given callback.
setClientStateChangeCallback :: ClientStateChangeCallback
                             -> ClientConfig -> ClientConfig
setClientStateChangeCallback :: ClientStateChangeCallback -> ClientConfig -> ClientConfig
setClientStateChangeCallback ClientStateChangeCallback
cb (ClientConfig Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig) =
    (Ptr C'netcode_client_config_t
 -> ClientCallbacks
 -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
ClientConfig ((Ptr C'netcode_client_config_t
  -> ClientCallbacks
  -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
 -> ClientConfig)
-> (Ptr C'netcode_client_config_t
    -> ClientCallbacks
    -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks' -> do
        (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks) <- Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks'
        C'state_change_callback_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'state_change_callback_t -> IO ())
-> C'state_change_callback_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'state_change_callback_t
clientStateChange ClientCallbacks
callbacks
        C'state_change_callback_t
fPtr <- ClientStateChangeCallback -> IO C'state_change_callback_t
mkClientStateChangeCallback ClientStateChangeCallback
cb
        C'netcode_client_config_t
config <- Ptr C'netcode_client_config_t -> IO C'netcode_client_config_t
forall a. Storable a => Ptr a -> IO a
peek Ptr C'netcode_client_config_t
configPtr
        Ptr C'netcode_client_config_t -> C'netcode_client_config_t -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr C'netcode_client_config_t
configPtr (C'netcode_client_config_t -> IO ())
-> C'netcode_client_config_t -> IO ()
forall a b. (a -> b) -> a -> b
$
            C'netcode_client_config_t
config { c'netcode_client_config_t'state_change_callback :: C'state_change_callback_t
c'netcode_client_config_t'state_change_callback = C'state_change_callback_t
fPtr }
        (Ptr C'netcode_client_config_t, ClientCallbacks)
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks { clientStateChange :: C'state_change_callback_t
clientStateChange = C'state_change_callback_t
fPtr })

-- | Clears the 'ClientStateChangeCallback' for the given config.
clearClientStateChangeCallback :: ClientConfig -> ClientConfig
clearClientStateChangeCallback :: ClientConfig -> ClientConfig
clearClientStateChangeCallback (ClientConfig Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig) =
    (Ptr C'netcode_client_config_t
 -> ClientCallbacks
 -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
ClientConfig ((Ptr C'netcode_client_config_t
  -> ClientCallbacks
  -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
 -> ClientConfig)
-> (Ptr C'netcode_client_config_t
    -> ClientCallbacks
    -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks' -> do
        (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks) <- Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks'
        C'state_change_callback_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'state_change_callback_t -> IO ())
-> C'state_change_callback_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'state_change_callback_t
clientStateChange ClientCallbacks
callbacks
        C'netcode_client_config_t
config <- Ptr C'netcode_client_config_t -> IO C'netcode_client_config_t
forall a. Storable a => Ptr a -> IO a
peek Ptr C'netcode_client_config_t
configPtr
        Ptr C'netcode_client_config_t -> C'netcode_client_config_t -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr C'netcode_client_config_t
configPtr (C'netcode_client_config_t -> IO ())
-> C'netcode_client_config_t -> IO ()
forall a b. (a -> b) -> a -> b
$
            C'netcode_client_config_t
config { c'netcode_client_config_t'state_change_callback :: C'state_change_callback_t
c'netcode_client_config_t'state_change_callback = C'state_change_callback_t
forall a. FunPtr a
nullFunPtr }
        (Ptr C'netcode_client_config_t, ClientCallbacks)
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks { clientStateChange :: C'state_change_callback_t
clientStateChange = C'state_change_callback_t
forall a. FunPtr a
nullFunPtr })

-- | Removes the existing send and receive overrides for the given config, if
-- set, and instead uses the ones given.
setClientSendReceiveOverrides :: SendPacketOverride
                              -> ReceivePacketOverride
                              -> ClientConfig -> ClientConfig
setClientSendReceiveOverrides :: SendPacketOverride
-> ReceivePacketOverride -> ClientConfig -> ClientConfig
setClientSendReceiveOverrides SendPacketOverride
sendFn ReceivePacketOverride
recvFn (ClientConfig Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig) =
    (Ptr C'netcode_client_config_t
 -> ClientCallbacks
 -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
ClientConfig ((Ptr C'netcode_client_config_t
  -> ClientCallbacks
  -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
 -> ClientConfig)
-> (Ptr C'netcode_client_config_t
    -> ClientCallbacks
    -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks' -> do
        (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks) <- Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks'
        C'send_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'send_packet_override_t -> IO ())
-> C'send_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'send_packet_override_t
clientSendPacketOverride ClientCallbacks
callbacks
        C'receive_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'receive_packet_override_t -> IO ())
-> C'receive_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'receive_packet_override_t
clientReceivePacketOverride ClientCallbacks
callbacks
        C'netcode_client_config_t
config <- Ptr C'netcode_client_config_t -> IO C'netcode_client_config_t
forall a. Storable a => Ptr a -> IO a
peek Ptr C'netcode_client_config_t
configPtr
        C'send_packet_override_t
sendOverride <- SendPacketOverride -> IO C'send_packet_override_t
mkSendPacketOverride SendPacketOverride
sendFn
        C'receive_packet_override_t
recvOverride <- ReceivePacketOverride -> IO C'receive_packet_override_t
mkReceivePacketOverride ReceivePacketOverride
recvFn
        Ptr C'netcode_client_config_t -> C'netcode_client_config_t -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr C'netcode_client_config_t
configPtr (C'netcode_client_config_t -> IO ())
-> C'netcode_client_config_t -> IO ()
forall a b. (a -> b) -> a -> b
$ C'netcode_client_config_t
config
            { c'netcode_client_config_t'send_packet_override :: C'send_packet_override_t
c'netcode_client_config_t'send_packet_override = C'send_packet_override_t
sendOverride
            , c'netcode_client_config_t'receive_packet_override :: C'receive_packet_override_t
c'netcode_client_config_t'receive_packet_override = C'receive_packet_override_t
recvOverride
            , c'netcode_client_config_t'override_send_and_receive :: CInt
c'netcode_client_config_t'override_send_and_receive = CInt
1
            }
        let newcbs :: ClientCallbacks
newcbs = ClientCallbacks
callbacks
              { clientSendPacketOverride :: C'send_packet_override_t
clientSendPacketOverride = C'send_packet_override_t
sendOverride
              , clientReceivePacketOverride :: C'receive_packet_override_t
clientReceivePacketOverride = C'receive_packet_override_t
recvOverride
              }
        (Ptr C'netcode_client_config_t, ClientCallbacks)
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
newcbs)

-- | Changes the config to use the default send and receive packet functions.
clearClientSendReceiveOverrides :: ClientConfig -> ClientConfig
clearClientSendReceiveOverrides :: ClientConfig -> ClientConfig
clearClientSendReceiveOverrides (ClientConfig Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig) =
    (Ptr C'netcode_client_config_t
 -> ClientCallbacks
 -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
ClientConfig ((Ptr C'netcode_client_config_t
  -> ClientCallbacks
  -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
 -> ClientConfig)
-> (Ptr C'netcode_client_config_t
    -> ClientCallbacks
    -> IO (Ptr C'netcode_client_config_t, ClientCallbacks))
-> ClientConfig
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks' -> do
        (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
callbacks) <- Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig Ptr C'netcode_client_config_t
configPtr' ClientCallbacks
callbacks'
        C'send_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'send_packet_override_t -> IO ())
-> C'send_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'send_packet_override_t
clientSendPacketOverride ClientCallbacks
callbacks
        C'receive_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'receive_packet_override_t -> IO ())
-> C'receive_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'receive_packet_override_t
clientReceivePacketOverride ClientCallbacks
callbacks
        C'netcode_client_config_t
config <- Ptr C'netcode_client_config_t -> IO C'netcode_client_config_t
forall a. Storable a => Ptr a -> IO a
peek Ptr C'netcode_client_config_t
configPtr
        Ptr C'netcode_client_config_t -> C'netcode_client_config_t -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr C'netcode_client_config_t
configPtr (C'netcode_client_config_t -> IO ())
-> C'netcode_client_config_t -> IO ()
forall a b. (a -> b) -> a -> b
$ C'netcode_client_config_t
config
            { c'netcode_client_config_t'send_packet_override :: C'send_packet_override_t
c'netcode_client_config_t'send_packet_override = C'send_packet_override_t
forall a. FunPtr a
nullFunPtr
            , c'netcode_client_config_t'receive_packet_override :: C'receive_packet_override_t
c'netcode_client_config_t'receive_packet_override = C'receive_packet_override_t
forall a. FunPtr a
nullFunPtr
            , c'netcode_client_config_t'override_send_and_receive :: CInt
c'netcode_client_config_t'override_send_and_receive = CInt
0
            }
        let newcbs :: ClientCallbacks
newcbs = ClientCallbacks
callbacks
              { clientSendPacketOverride :: C'send_packet_override_t
clientSendPacketOverride = C'send_packet_override_t
forall a. FunPtr a
nullFunPtr
              , clientReceivePacketOverride :: C'receive_packet_override_t
clientReceivePacketOverride = C'receive_packet_override_t
forall a. FunPtr a
nullFunPtr
              }
        (Ptr C'netcode_client_config_t, ClientCallbacks)
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_config_t
configPtr, ClientCallbacks
newcbs)

-- | Creates a client at the given address using the provided config. Throws an
-- IOException on failure.
--
-- Note, the address used here can be either formatted as an IPv4 address or an
-- IPv6 address, similar to the arguments passed to 'parseAddress'. In the
-- common case, you will likely want to use INADDR_ANY to bind to the
-- underlying socket, which is represented by the address "0.0.0.0"
--
-- The time passed to this create function should be a measurement in seconds,
-- such that when connecting in the future using 'updateClient', the same
-- resolution timer is being passed. That allows the library to properly
-- timeout in cases where connections are taking too long to establish.
createClient :: String -> ClientConfig -> Double -> IO Client
createClient :: String -> ClientConfig -> Double -> IO Client
createClient String
s (ClientConfig Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig) Double
time = (Ptr C'netcode_client_config_t -> IO Client) -> IO Client
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr C'netcode_client_config_t -> IO Client) -> IO Client)
-> (Ptr C'netcode_client_config_t -> IO Client) -> IO Client
forall a b. (a -> b) -> a -> b
$ \Ptr C'netcode_client_config_t
clientConfig -> do
    (Ptr C'netcode_client_config_t
config, ClientCallbacks
callbacks) <- Ptr C'netcode_client_config_t
-> ClientCallbacks
-> IO (Ptr C'netcode_client_config_t, ClientCallbacks)
mkConfig Ptr C'netcode_client_config_t
clientConfig ClientCallbacks
defaultClientCallbacks
    Ptr C'netcode_client_t
ptr <- String
-> (CString -> IO (Ptr C'netcode_client_t))
-> IO (Ptr C'netcode_client_t)
forall a. String -> (CString -> IO a) -> IO a
withCString String
s ((CString -> IO (Ptr C'netcode_client_t))
 -> IO (Ptr C'netcode_client_t))
-> (CString -> IO (Ptr C'netcode_client_t))
-> IO (Ptr C'netcode_client_t)
forall a b. (a -> b) -> a -> b
$ \CString
cs ->
        CString
-> Ptr C'netcode_client_config_t
-> CDouble
-> IO (Ptr C'netcode_client_t)
c'netcode_client_create CString
cs Ptr C'netcode_client_config_t
config (Double -> CDouble
CDouble Double
time)
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Ptr C'netcode_client_t
ptr Ptr C'netcode_client_t -> Ptr C'netcode_client_t -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr C'netcode_client_t
forall a. Ptr a
nullPtr) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Failed to create client!"
    Client -> IO Client
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr C'netcode_client_t -> ClientCallbacks -> Client
Client Ptr C'netcode_client_t
ptr ClientCallbacks
callbacks)

-- | Destroys the client and frees all of the Haskell-side function pointers
-- that were registered as callbacks.
destroyClient :: Client -> IO ()
destroyClient :: Client -> IO ()
destroyClient (Client Ptr C'netcode_client_t
c ClientCallbacks
cbs) = do
    Ptr C'netcode_client_t -> IO ()
c'netcode_client_destroy Ptr C'netcode_client_t
c
    C'state_change_callback_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'state_change_callback_t -> IO ())
-> C'state_change_callback_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'state_change_callback_t
clientStateChange ClientCallbacks
cbs
    C'send_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'send_packet_override_t -> IO ())
-> C'send_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'send_packet_override_t
clientSendPacketOverride ClientCallbacks
cbs
    C'receive_packet_override_t -> IO ()
forall a. FunPtr a -> IO ()
freeNullFunPtr (C'receive_packet_override_t -> IO ())
-> C'receive_packet_override_t -> IO ()
forall a b. (a -> b) -> a -> b
$ ClientCallbacks -> C'receive_packet_override_t
clientReceivePacketOverride ClientCallbacks
cbs

-- | Generates a random 64-bit client ID to be used with 'generateConnectToken'
generateClientID :: IO Word64
generateClientID :: IO Word64
generateClientID = (Ptr Word64 -> IO Word64) -> IO Word64
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Word64 -> IO Word64) -> IO Word64)
-> (Ptr Word64 -> IO Word64) -> IO Word64
forall a b. (a -> b) -> a -> b
$ \Ptr Word64
idPtr -> do
    Ptr Word8 -> CInt -> IO ()
c'netcode_random_bytes (Ptr Word64 -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr Ptr Word64
idPtr) (CInt -> IO ()) -> CInt -> IO ()
forall a b. (a -> b) -> a -> b
$
        Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> Int -> CInt
forall a b. (a -> b) -> a -> b
$ Word64 -> Int
forall a. Storable a => a -> Int
sizeOf (Word64
forall a. HasCallStack => a
undefined :: Word64)
    Ptr Word64 -> IO Word64
forall a. Storable a => Ptr a -> IO a
peek Ptr Word64
idPtr

-- | Begin the process to connect the client to a server stored in the given
-- 'ConnectToken'. This does not connect the client immediately, but rather
-- resets the client object and sets the state to
-- 'ClientState'SendingConnectionRequest'. The client will attempt to connect
-- on the next call to 'updateClient'.
connectClient :: Client -> ConnectToken -> IO ()
connectClient :: Client -> ConnectToken -> IO ()
connectClient (Client Ptr C'netcode_client_t
c ClientCallbacks
_) (ConnectToken ForeignPtr Word8
ctPtr) =
    ForeignPtr Word8 -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
ctPtr (Ptr C'netcode_client_t -> Ptr Word8 -> IO ()
c'netcode_client_connect Ptr C'netcode_client_t
c)

-- | Disconnects the client from anything it might be connected to.
disconnectClient :: Client -> IO ()
disconnectClient :: Client -> IO ()
disconnectClient (Client Ptr C'netcode_client_t
c ClientCallbacks
_) = Ptr C'netcode_client_t -> IO ()
c'netcode_client_disconnect Ptr C'netcode_client_t
c

-- | Main processing call for clients with the current time in seconds (in the
-- same domain as the time passed to 'createClient'). This flushes packet
-- queues at the appropriate rate and updates connection statuses among other
-- things. It is expected to be called in the main loop of the application.
updateClient :: Client -> Double -> IO ()
updateClient :: Client -> Double -> IO ()
updateClient (Client Ptr C'netcode_client_t
c ClientCallbacks
_) = Ptr C'netcode_client_t -> CDouble -> IO ()
c'netcode_client_update Ptr C'netcode_client_t
c (CDouble -> IO ()) -> (Double -> CDouble) -> Double -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> CDouble
CDouble

-- | Returns the current state of the 'Client'.
getClientState :: Client -> IO ClientState
getClientState :: Client -> IO ClientState
getClientState (Client Ptr C'netcode_client_t
c ClientCallbacks
_) = CInt -> ClientState
typedClientState (CInt -> ClientState) -> IO CInt -> IO ClientState
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr C'netcode_client_t -> IO CInt
c'netcode_client_state Ptr C'netcode_client_t
c

-- | Returns true if the 'Client' is in a state considered to be disconnected,
-- as opposed to connected or connecting.
isClientDisconnected :: Client -> IO Bool
isClientDisconnected :: Client -> IO Bool
isClientDisconnected (Client Ptr C'netcode_client_t
c ClientCallbacks
_) =
    (CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
<= CInt
forall a. Num a => a
c'NETCODE_CLIENT_STATE_DISCONNECTED) (CInt -> Bool) -> IO CInt -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr C'netcode_client_t -> IO CInt
c'netcode_client_state Ptr C'netcode_client_t
c

-- | Returns the sequence number of the next packet that the 'Client' will
-- send.
nextClientPacketSequence :: Client -> IO Word64
nextClientPacketSequence :: Client -> IO Word64
nextClientPacketSequence (Client Ptr C'netcode_client_t
c ClientCallbacks
_) = Ptr C'netcode_client_t -> IO Word64
c'netcode_client_next_packet_sequence Ptr C'netcode_client_t
c

-- | Enqueues a packet to be sent during the next call to 'updateClient'.
sendPacketFromClient :: Client -> Int -> Ptr Word8 -> IO ()
sendPacketFromClient :: Client -> Int -> Ptr Word8 -> IO ()
sendPacketFromClient (Client Ptr C'netcode_client_t
c ClientCallbacks
_) Int
pktSz Ptr Word8
pktMem = do
    let pktSize :: CInt
pktSize = CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
min CInt
forall a. Num a => a
c'NETCODE_MAX_PACKET_SIZE (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
pktSz)
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
pktSz Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
forall a. Num a => a
c'NETCODE_MAX_PACKET_SIZE) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
        String
"WARNING: Sending packet that's too large: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
pktSz
    Ptr C'netcode_client_t -> Ptr Word8 -> CInt -> IO ()
c'netcode_client_send_packet Ptr C'netcode_client_t
c Ptr Word8
pktMem CInt
pktSize

-- | Dequeues a received packet from the t'Netcode.IO.Server'. This function
-- returns a @Just@ until the queue is empty, upon which it will return
-- @Nothing@.
receivePacketFromServer :: Client -> IO (Maybe Packet)
receivePacketFromServer :: Client -> IO (Maybe Packet)
receivePacketFromServer (Client Ptr C'netcode_client_t
c ClientCallbacks
_) =
    (Ptr Word64 -> IO (Maybe Packet)) -> IO (Maybe Packet)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Word64 -> IO (Maybe Packet)) -> IO (Maybe Packet))
-> (Ptr Word64 -> IO (Maybe Packet)) -> IO (Maybe Packet)
forall a b. (a -> b) -> a -> b
$ \Ptr Word64
sequenceNumPtr ->
    (Ptr CInt -> IO (Maybe Packet)) -> IO (Maybe Packet)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CInt -> IO (Maybe Packet)) -> IO (Maybe Packet))
-> (Ptr CInt -> IO (Maybe Packet)) -> IO (Maybe Packet)
forall a b. (a -> b) -> a -> b
$ \Ptr CInt
pktSzPtr -> do
        Ptr Word8
packetMem <- Ptr C'netcode_client_t -> Ptr CInt -> Ptr Word64 -> IO (Ptr Word8)
c'netcode_client_receive_packet Ptr C'netcode_client_t
c Ptr CInt
pktSzPtr Ptr Word64
sequenceNumPtr
        let finalizer :: IO ()
finalizer = Ptr C'netcode_client_t -> Ptr () -> IO ()
c'netcode_client_free_packet Ptr C'netcode_client_t
c (Ptr Word8 -> Ptr ()
forall a b. Ptr a -> Ptr b
castPtr Ptr Word8
packetMem)
        if Ptr Word8
packetMem Ptr Word8 -> Ptr Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr Word8
forall a. Ptr a
nullPtr
            then Maybe Packet -> IO (Maybe Packet)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Packet
forall a. Maybe a
Nothing
            else (Packet -> Maybe Packet) -> IO Packet -> IO (Maybe Packet)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Packet -> Maybe Packet
forall a. a -> Maybe a
Just (IO Packet -> IO (Maybe Packet)) -> IO Packet -> IO (Maybe Packet)
forall a b. (a -> b) -> a -> b
$
                Word64 -> Int -> ForeignPtr Word8 -> Packet
Packet (Word64 -> Int -> ForeignPtr Word8 -> Packet)
-> IO Word64 -> IO (Int -> ForeignPtr Word8 -> Packet)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Word64 -> IO Word64
forall a. Storable a => Ptr a -> IO a
peek Ptr Word64
sequenceNumPtr
                       IO (Int -> ForeignPtr Word8 -> Packet)
-> IO Int -> IO (ForeignPtr Word8 -> Packet)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> IO CInt -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CInt -> IO CInt
forall a. Storable a => Ptr a -> IO a
peek Ptr CInt
pktSzPtr)
                       IO (ForeignPtr Word8 -> Packet)
-> IO (ForeignPtr Word8) -> IO Packet
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Ptr Word8 -> IO () -> IO (ForeignPtr Word8)
forall a. Ptr a -> IO () -> IO (ForeignPtr a)
newForeignPtr Ptr Word8
packetMem IO ()
finalizer

-- | Returns the port assigned to this 'Client'.
getClientPort :: Client -> IO Word16
getClientPort :: Client -> IO Word16
getClientPort (Client Ptr C'netcode_client_t
c ClientCallbacks
_) = Ptr C'netcode_client_t -> IO Word16
c'netcode_client_get_port Ptr C'netcode_client_t
c

-- | Performs an action with the address of the server to which the given
-- 'Client' is connected to. This is meant to minimize the chances that the
-- 'Address' value will be used in a manner that outlives the given 'Client'.
-- Callers should avoid storing the 'Address' value or returning it as a result
-- of this function.
--
-- In the event that the client is not connected to a server, the address
-- passed to the action will be @0.0.0.0@.
withClientServerAddress :: Client -> (Address -> IO a) -> IO a
withClientServerAddress :: Client -> (Address -> IO a) -> IO a
withClientServerAddress (Client Ptr C'netcode_client_t
c ClientCallbacks
_) Address -> IO a
fn = do
    Ptr C'netcode_address_t
aptr <- Ptr C'netcode_client_t -> IO (Ptr C'netcode_address_t)
c'netcode_client_server_address Ptr C'netcode_client_t
c
    if Ptr C'netcode_address_t
aptr Ptr C'netcode_address_t -> Ptr C'netcode_address_t -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr C'netcode_address_t
forall a. Ptr a
nullPtr
        then String -> IO Address
parseAddress String
"0.0.0.0" IO Address -> (Address -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Address -> IO a
fn
        else ForeignPtr C'netcode_address_t -> Address
Address (ForeignPtr C'netcode_address_t -> Address)
-> IO (ForeignPtr C'netcode_address_t) -> IO Address
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr C'netcode_address_t -> IO (ForeignPtr C'netcode_address_t)
forall a. Ptr a -> IO (ForeignPtr a)
newForeignPtr_ Ptr C'netcode_address_t
aptr IO Address -> (Address -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Address -> IO a
fn

-- | A 'ConnectToken' represents an encrypted set of data fields that describe
-- both the client requesting to make a connection and the available servers to
-- which that connection can be made. It is generated solely via the 
newtype ConnectToken = ConnectToken (ForeignPtr Word8)

-- | Gives the maximum size, in bytes, of user data stored in a 'ConnectToken'.
maximumUserDataSize :: Num a => a
maximumUserDataSize :: a
maximumUserDataSize = a
forall a. Num a => a
c'NETCODE_USER_DATA_BYTES

-- | Returns the maximum number of servers that can be stored in a
-- 'ConnectToken'.
maximumServersPerConnect :: Num a => a
maximumServersPerConnect :: a
maximumServersPerConnect = a
forall a. Num a => a
c'NETCODE_MAX_SERVERS_PER_CONNECT

-- | Returns the number of bytes expected in the private key used to generate a
-- 'ConnectToken'
privateKeySize :: Num a => a
privateKeySize :: a
privateKeySize = a
forall a. Num a => a
c'NETCODE_KEY_BYTES

-- | Creates a connect token for the given client (by clientID) with the list
-- of associated addresses. User data may be at most 'maximumUserDataSize'
-- values, otherwise is truncated or zero-padded to fill. The list of public
-- and internal servers must not be empty and may contain at most
-- 'maximumServersPerConnect' values, otherwise is truncated. Throws an
-- IOException on failure.
generateConnectToken :: [(String, String)] -- ^ Public and internal servers
                     -> Int                -- ^ Token expiration in seconds
                     -> Int                -- ^ Token timeout in seconds
                     -> Word64             -- ^ Unique Client ID
                     -> Word64             -- ^ Protocol ID
                     -> [Word8]            -- ^ Private key
                     -> [Word8]            -- ^ User data
                     -> IO ConnectToken
generateConnectToken :: [(String, String)]
-> Int
-> Int
-> Word64
-> Word64
-> [Word8]
-> [Word8]
-> IO ConnectToken
generateConnectToken [] Int
_ Int
_ Word64
_ Word64
_ [Word8]
_ [Word8]
_ = String -> IO ConnectToken
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Connect token server list is empty."
generateConnectToken [(String, String)]
addrs Int
expiry Int
timeout Word64
clientID Word64
protocolID [Word8]
privateKey [Word8]
userData =
    Int -> (Ptr CString -> IO ConnectToken) -> IO ConnectToken
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray ([(String, String)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, String)]
addrs) ((Ptr CString -> IO ConnectToken) -> IO ConnectToken)
-> (Ptr CString -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \Ptr CString
externalAddrs ->
    Int -> (Ptr CString -> IO ConnectToken) -> IO ConnectToken
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray ([(String, String)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, String)]
addrs) ((Ptr CString -> IO ConnectToken) -> IO ConnectToken)
-> (Ptr CString -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \Ptr CString
internalAddrs ->
        let checkServers :: IO ()
checkServers =
                Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([(String, String)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, String)]
addrs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
forall a. Num a => a
maximumServersPerConnect) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
                String
"Warning: Too many servers passed to connect token: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show ([(String, String)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, String)]
addrs)

            writeAddrsAndGo :: Int -> [(String, String)] -> IO ConnectToken
writeAddrsAndGo Int
i ((String
s1, String
s2) : [(String, String)]
rest) = String -> (CString -> IO ConnectToken) -> IO ConnectToken
forall a. String -> (CString -> IO a) -> IO a
withCString String
s1 ((CString -> IO ConnectToken) -> IO ConnectToken)
-> (CString -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \CString
cs1 -> do
                Ptr CString -> Int -> CString -> IO ()
forall a. Storable a => Ptr a -> Int -> a -> IO ()
pokeElemOff Ptr CString
externalAddrs Int
i CString
cs1
                String -> (CString -> IO ConnectToken) -> IO ConnectToken
forall a. String -> (CString -> IO a) -> IO a
withCString String
s2 ((CString -> IO ConnectToken) -> IO ConnectToken)
-> (CString -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \CString
cs2 -> do
                    Ptr CString -> Int -> CString -> IO ()
forall a. Storable a => Ptr a -> Int -> a -> IO ()
pokeElemOff Ptr CString
internalAddrs Int
i CString
cs2
                    Int -> [(String, String)] -> IO ConnectToken
writeAddrsAndGo (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [(String, String)]
rest
            writeAddrsAndGo Int
_ [] =  -- go....
                Int -> (Ptr Word8 -> IO ConnectToken) -> IO ConnectToken
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray Int
forall a. Num a => a
c'NETCODE_USER_DATA_BYTES ((Ptr Word8 -> IO ConnectToken) -> IO ConnectToken)
-> (Ptr Word8 -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
userDataBytes ->
                Int -> (Ptr Word8 -> IO ConnectToken) -> IO ConnectToken
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray Int
forall a. Num a => a
c'NETCODE_KEY_BYTES ((Ptr Word8 -> IO ConnectToken) -> IO ConnectToken)
-> (Ptr Word8 -> IO ConnectToken) -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
privateKeyBytes -> do
                    ForeignPtr Word8
connectTokenPtr <- Int -> IO (ForeignPtr Word8)
forall a. Int -> IO (ForeignPtr a)
mallocForeignPtrBytes Int
forall a. Num a => a
c'NETCODE_CONNECT_TOKEN_BYTES
                    Ptr Word8 -> [Word8] -> IO ()
forall a. Storable a => Ptr a -> [a] -> IO ()
pokeArray Ptr Word8
privateKeyBytes (Int -> [Word8] -> [Word8]
forall a. Int -> [a] -> [a]
take Int
forall a. Num a => a
c'NETCODE_KEY_BYTES       ([Word8] -> [Word8]) -> [Word8] -> [Word8]
forall a b. (a -> b) -> a -> b
$ [Word8]
privateKey [Word8] -> [Word8] -> [Word8]
forall a. Semigroup a => a -> a -> a
<> Word8 -> [Word8]
forall a. a -> [a]
repeat Word8
0)
                    Ptr Word8 -> [Word8] -> IO ()
forall a. Storable a => Ptr a -> [a] -> IO ()
pokeArray Ptr Word8
userDataBytes   (Int -> [Word8] -> [Word8]
forall a. Int -> [a] -> [a]
take Int
forall a. Num a => a
c'NETCODE_USER_DATA_BYTES ([Word8] -> [Word8]) -> [Word8] -> [Word8]
forall a b. (a -> b) -> a -> b
$ [Word8]
userData   [Word8] -> [Word8] -> [Word8]
forall a. Semigroup a => a -> a -> a
<> Word8 -> [Word8]
forall a. a -> [a]
repeat Word8
0)
                    CInt
result <- ForeignPtr Word8 -> (Ptr Word8 -> IO CInt) -> IO CInt
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
connectTokenPtr ((Ptr Word8 -> IO CInt) -> IO CInt)
-> (Ptr Word8 -> IO CInt) -> IO CInt
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
connectTokenBytes ->
                        CInt
-> Ptr CString
-> Ptr CString
-> CInt
-> CInt
-> Word64
-> Word64
-> Ptr Word8
-> Ptr Word8
-> Ptr Word8
-> IO CInt
c'netcode_generate_connect_token (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> Int -> CInt
forall a b. (a -> b) -> a -> b
$ [(String, String)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, String)]
addrs)
                                                         Ptr CString
externalAddrs
                                                         Ptr CString
internalAddrs
                                                         (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
expiry)
                                                         (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
timeout)
                                                         Word64
clientID
                                                         Word64
protocolID
                                                         Ptr Word8
privateKeyBytes
                                                         Ptr Word8
userDataBytes
                                                         Ptr Word8
connectTokenBytes
                    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (CInt
result CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
forall a. Num a => a
c'NETCODE_ERROR) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Error generating connect token"
                    ConnectToken -> IO ConnectToken
forall (m :: * -> *) a. Monad m => a -> m a
return (ConnectToken -> IO ConnectToken)
-> ConnectToken -> IO ConnectToken
forall a b. (a -> b) -> a -> b
$ ForeignPtr Word8 -> ConnectToken
ConnectToken ForeignPtr Word8
connectTokenPtr
          in IO ()
checkServers IO () -> IO ConnectToken -> IO ConnectToken
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> [(String, String)] -> IO ConnectToken
writeAddrsAndGo Int
0 [(String, String)]
addrs