Copyright | (c) Anton Gushcha, 2015-2016 |
---|---|
License | BSD3 |
Maintainer | ncrashed@gmail.com |
Stability | experimental |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
The core module contains high-level networking API for Gore&Ash. It allows to define separate types of messages for each actor and perform automatic synchronzation controlled by synchronization EDSL.
The module depends on following core modules:
- actor - Game.GoreAndAsh.Actor
- logging - Game.GoreAndAsh.Logging
- network - Game.GoreAndAsh.Network
So SyncT
should be placed after LoggingT
, ActorT
and NetworkT
in monad stack.
The module is NOT pure within first phase (see ModuleStack
docs), therefore currently only IO
end monad can handler the module.
Example of embedding:
-- | Application monad is monad stack build from given list of modules over base monad (IO) type AppStack = ModuleStack [LoggingT, NetworkT, ActorT, SyncT ... other modules ... ] IO newtype AppState = AppState (ModuleState AppStack) deriving (Generic) instance NFData AppState -- | Wrapper around type family newtype AppMonad a = AppMonad (AppStack a) deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadThrow, MonadCatch, LoggingMonad, NetworkMonad, ActorMonad, SyncMonad ... other modules monads ... ) -- | Current GHC (7.10.3) isn't able to derive this instance SyncMonad AppMonad where getSyncIdM = AppMonad . getSyncIdM getSyncTypeRepM = AppMonad . getSyncTypeRepM registerSyncIdM = AppMonad . registerSyncIdM addSyncTypeRepM a b = AppMonad $ addSyncTypeRepM a b syncScheduleMessageM peer ch i mt msg = AppMonad $ syncScheduleMessageM peer ch i mt msg syncSetLoggingM = AppMonad . syncSetLoggingM syncSetRoleM = AppMonad . syncSetRoleM syncGetRoleM = AppMonad syncGetRoleM syncRequestIdM a b = AppMonad $ syncRequestIdM a b instance GameModule AppMonad AppState where type ModuleState AppMonad = AppState runModule (AppMonad m) (AppState s) = do (a, s') <- runModule m s return (a, AppState s') newModuleState = AppState $ newModuleState withModule _ = withModule (Proxy :: Proxy AppStack) cleanupModule (AppState s) = cleanupModule s -- | Arrow that is build over the monad stack type AppWire a b = GameWire AppMonad a b -- | Action that makes indexed app wire type AppActor i a b = GameActor AppMonad i a b
Important note, the system tries to use channel id 1 for service messages, but fallbacks to default channel if there is only one channel allocated in network module. Check initalization of network module, client and server allocated channels count must match.
- data SyncState s
- data SyncT s m a
- data SyncRole
- class MonadIO m => SyncMonad m where
- class ActorMessage i => NetworkMessage i where
- type NetworkMessageType i :: *
- peerIndexedMessages :: forall m i a. (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> GameWire m a (Event (Seq (NetworkMessageType i)))
- peerProcessIndexed :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> (a -> NetworkMessageType i -> a) -> GameWire m a a
- peerProcessIndexedM :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> (a -> NetworkMessageType i -> GameMonadT m a) -> GameWire m a a
- peerSendIndexedM :: forall m i. (SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> MessageType -> NetworkMessageType i -> m ()
- peerSendIndexed :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> MessageType -> GameWire m (Event (NetworkMessageType i)) (Event ())
- peerSendIndexedDyn :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => ChannelID -> MessageType -> GameWire m (Event (Peer, i, NetworkMessageType i)) (Event ())
- peerSendIndexedMany :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i), Foldable t) => Peer -> ChannelID -> i -> MessageType -> GameWire m (Event (t (NetworkMessageType i))) (Event ())
- peerSendIndexedManyDyn :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i), Foldable t) => ChannelID -> MessageType -> GameWire m (Event (t (Peer, i, NetworkMessageType i))) (Event ())
- filterMsgs :: Monad m => (a -> Bool) -> GameWire m (Event (Seq a)) (Event (Seq a))
- class NetworkMessage i => RemoteActor i a | i -> a, a -> i where
- type RemoteActorState i :: *
- type RemoteActorId a :: *
- clientSync :: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, RemoteActor i a) => Sync m i s a -> Peer -> i -> GameWire m s a
- serverSync :: (MonadFix m, ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, RemoteActor i a) => Sync m i s a -> i -> GameWire m s a
- data Sync m i s a
- type FullSync m i s = Sync m i s s
- noSync :: (s -> a) -> Sync m i s a
- clientSide :: (Eq a, Serialize a, RemoteActor i s) => Peer -> Word64 -> (s -> a) -> Sync m i s a
- serverSide :: (Serialize a, RemoteActor i s) => Word64 -> (s -> a) -> Sync m i s a
- condSync :: Monad m => GameWire m s (Event b) -> (s -> a) -> Sync m i s a -> Sync m i s a
- syncReject :: (Serialize a, RemoteActor i s) => GameWire m (s, a) (Event a) -> Word64 -> Sync m i s a -> Sync m i s a
- fieldChanges :: Eq a => (s -> a) -> GameWire m s (Event a)
- fieldChangesWithin :: (Num a, Ord a) => (s -> a) -> a -> GameWire m s (Event a)
- newtype RemActorCollId = RemActorCollId {}
- remoteActorCollectionServer :: forall m a b c c2 i. (MonadFix m, SyncMonad m, LoggingMonad m, ActorMonad m, NetworkMonad m, Eq i, RemoteActor i b, DynCollection c, FilterConstraint c (GameWireIndexed m i a b), FilterConstraint c (Either () (b, i)), Foldable c2, Functor c2) => c (GameActor m i a b) -> GameActor m RemActorCollId (a, Event (c (GameActor m i a b)), Event (c2 i)) (c b)
- remoteActorCollectionClient :: forall m i a b. (SyncMonad m, LoggingMonad m, ActorMonad m, NetworkMonad m, Eq i, RemoteActor i b) => RemActorCollId -> Peer -> (i -> GameActor m i a b) -> GameActor m RemActorCollId a (Seq b)
Low-level API
Inner state of sync module
s
- - State of next module, the states are chained via nesting.
Monad transformer of sync core module.
s
- - State of next core module in modules chain;
m
- - Next monad in modules monad stack;
a
- - Type of result value;
How to embed module:
type AppStack = ModuleStack [LoggingT, NetworkT, ActorT, SyncT, ... other modules ... ] IO -- | Current GHC (7.10.3) isn't able to derive this instance SyncMonad AppMonad where getSyncIdM = AppMonad . getSyncIdM getSyncTypeRepM = AppMonad . getSyncTypeRepM registerSyncIdM = AppMonad . registerSyncIdM addSyncTypeRepM a b = AppMonad $ addSyncTypeRepM a b syncScheduleMessageM peer ch i mt msg = AppMonad $ syncScheduleMessageM peer ch i mt msg syncSetLoggingM = AppMonad . syncSetLoggingM syncSetRoleM = AppMonad . syncSetRoleM syncGetRoleM = AppMonad syncGetRoleM syncRequestIdM a b = AppMonad $ syncRequestIdM a b newtype AppMonad a = AppMonad (AppStack a) deriving (Functor, Applicative, Monad, MonadFix, MonadIO, LoggingMonad, MonadThrow, MonadCatch, NetworkMonad, ActorMonad)
The module is NOT pure within first phase (see ModuleStack
docs), therefore currently only IO
end monad can handler the module.
MonadTrans (SyncT s) Source # | |
Monad m => MonadState (SyncState s) (SyncT s m) Source # | |
Monad m => Monad (SyncT s m) Source # | |
Functor m => Functor (SyncT s m) Source # | |
MonadFix m => MonadFix (SyncT s m) Source # | |
Monad m => Applicative (SyncT s m) Source # | |
MonadIO m => MonadIO (SyncT s m) Source # | |
MonadMask m => MonadMask (SyncT s m) Source # | |
MonadThrow m => MonadThrow (SyncT s m) Source # | |
MonadCatch m => MonadCatch (SyncT s m) Source # | |
MonadIO m => SyncMonad (SyncT s m) Source # | |
type ModuleState (SyncT s m) Source # | |
Defines behavior in synchronization for actor ids
SyncMaster | Registers types of actors |
SyncSlave | Always ask for ids from other nodes |
class MonadIO m => SyncMonad m where Source #
Low level API for module Need at least one network channel to operate. If you open more than one channel, the module would use chanel id 1 as service channel, therefore count of channels on client and server should match (server won't response on channel 1 if it doesn't have it).
getSyncIdM, getSyncTypeRepM, registerSyncIdM, addSyncTypeRepM, syncScheduleMessageM, syncSetLoggingM, syncSetRoleM, syncGetRoleM, syncRequestIdM
getSyncIdM :: HashableTypeRep -> m (Maybe Word64) Source #
Find actor id by it stable type representation
getSyncTypeRepM :: Word64 -> m (Maybe HashableTypeRep) Source #
Find actor type representation by it id
registerSyncIdM :: LoggingMonad m => HashableTypeRep -> m Word64 Source #
Generate and register new id for given actor type representation
addSyncTypeRepM :: LoggingMonad m => HashableTypeRep -> Word64 -> m () Source #
Register new type rep with given id, doesn't overide existing records
syncScheduleMessageM :: (NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) => Peer -> ChannelID -> i -> MessageType -> NetworkMessageType i -> m () Source #
Send message as soon as network id of actor is resolved
syncSetLoggingM :: Bool -> m () Source #
Switch on/off detailed logging of the module
syncSetRoleM :: SyncRole -> m () Source #
Setups behavior model in synchronizing of actor ids Note: clients should be slaves and server master
syncGetRoleM :: m SyncRole Source #
Returns current behavior model in synchronizing of actor ids Note: clients should be slaves and server master
syncRequestIdM :: forall proxy i. (ActorMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i) => Peer -> proxy i -> m () Source #
Send request for given peer for id of given actor
Typed message API
Example of usage of typed message API:
data Player = Player { playerId :: !PlayerId , playerPos :: !(V2 Double) , playerSize :: !Double } deriving (Generic) instance NFData Player newtype PlayerId = PlayerId { unPlayerId :: Int } deriving (Eq, Show, Generic) instance NFData PlayerId instance Hashable PlayerId instance Serialize PlayerId -- | Local message type data PlayerMessage = -- | The player was shot by specified player PlayerShotMessage !PlayerId deriving (Typeable, Generic) instance NFData PlayerMessage instance ActorMessage PlayerId where type ActorMessageType PlayerId = PlayerMessage toCounter = unPlayerId fromCounter = PlayerId -- | Remote message type data PlayerNetMessage = NetMsgPlayerFire !(V2 Double) deriving (Generic, Show) instance NFData PlayerNetMessage instance Serialize PlayerNetMessage instance NetworkMessage PlayerId where type NetworkMessageType PlayerId = PlayerNetMessage playerActorServer :: :: (PlayerId -> Player) -> AppActor PlayerId Game Player playerActorServer initialPlayer = makeActor $ i -> stateWire (initialPlayer i) $ mainController i where mainController i = proc (g, p) -> do p2 <- peerProcessIndexedM peer (ChannelID 0) i netProcess -< p forceNF . playerShot -< p2 where -- | Shortcut for peer peer = playerPeer $ initialPlayer i -- | Handle when player is shot playerShot :: AppWire Player Player playerShot = proc p -> do emsg <- actorMessages i isPlayerShotMessage -< () let newPlayer = p { playerPos = 0 } returnA -< event p (const newPlayer) emsg -- | Process player specific net messages netProcess :: Player -> PlayerNetMessage -> GameMonadT AppMonad Player netProcess p msg = case msg of NetMsgPlayerFire v -> do let d = normalize v v2 a = V2 a a pos = playerPos p + d * v2 (playerSize p * 1.5) vel = d * v2 bulletSpeed putMsgLnM $ "Fire bullet at " <> pack (show pos) <> " with velocity " <> pack (show vel) actors <- calculatePlayersOnLine pos vel forM_ actors . actorSendM actor . PlayerShotMessage . playerId $ p return p playerActorClient :: Peer -> PlayerId -> AppActor PlayerId Camera Player playerActorClient peer i = makeFixedActor i $ stateWire initialPlayer $ proc (c, p) -> do processFire -< (c, p) liftGameMonad4 renderSquare -< (playerSize p, playerPos p, playerColor p, c) forceNF -< p where initialPlayer = Player { playerId = i , playerPos = 0 , playerColor = V3 1 0 0 , playerRot = 0 , playerSpeed = 0.5 , playerSize = 1 } processFire :: AppWire (Camera, Player) () processFire = proc (c, p) -> do e <- mouseClick ButtonLeft -< () let wpos = cameraToWorld c $ e let edir = (v -> normalize $ v - playerPos p) $ wpos let emsg = NetMsgPlayerFire $ edir peerSendIndexed peer (ChannelID 0) i ReliableMessage -< emsg returnA -< ()
class ActorMessage i => NetworkMessage i Source #
Extension for actor message, messages that are sent to remote host
type NetworkMessageType i :: * Source #
Corresponding message payload for i
identifier, usually ADT
Getting messages
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> Peer | Which peer we are listening |
-> ChannelID | Which channel we are listening |
-> i | ID of actor |
-> GameWire m a (Event (Seq (NetworkMessageType i))) | Messages that are addressed to the actor |
Fires when network messages for specific actor has arrived Note: mid-level API is not safe to use with low-level at same time as first bytes of formed message are used for actor id. So, you need to have a special forbidden id for you custom messages.
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> Peer | Which peer we are listening |
-> ChannelID | Which channel we are listening |
-> i | ID of actor |
-> (a -> NetworkMessageType i -> a) | Handler of message |
-> GameWire m a a | Updates |
Same as peerIndexedMessages
, but transforms input state with given handler
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> Peer | Which peer we are listening |
-> ChannelID | Which channel we are listening |
-> i | ID of actor |
-> (a -> NetworkMessageType i -> GameMonadT m a) | Handler of message |
-> GameWire m a a | Updates |
Same as peerIndexedMessages
, but transforms input state with given handler, monadic version
Sending messages
:: (SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> Peer | Which peer we sending to |
-> ChannelID | Which channel we are sending within |
-> i | ID of actor |
-> MessageType | Strategy of the message (reliable, unordered etc.) |
-> NetworkMessageType i | Message to send |
-> m () |
Encodes a message for specific actor type and send it to remote host Note: mid-level API is not safe to use with low-level at same time as first bytes of formed message are used for actor id. So, you need to have a special forbidden id for you custom messages.
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> Peer | Which peer we sending to |
-> ChannelID | Which channel we are sending within |
-> i | ID of actor |
-> MessageType | Strategy of the message (reliable, unordered etc.) |
-> GameWire m (Event (NetworkMessageType i)) (Event ()) |
Encodes a message for specific actor type and send it to remote host, arrow version Note: mid-level API is not safe to use with low-level at same time as first bytes of formed message are used for actor id. So, you need to have a special forbidden id for you custom messages.
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i)) | |
=> ChannelID | Which channel we are sending within |
-> MessageType | Strategy of the message (reliable, unordered etc.) |
-> GameWire m (Event (Peer, i, NetworkMessageType i)) (Event ()) |
Encodes a message for specific actor type and send it to remote host, arrow version. Takes peer, id and message as arrow input.
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i), Foldable t) | |
=> Peer | Which peer we sending to |
-> ChannelID | Which channel we are sending within |
-> i | ID of actor |
-> MessageType | Strategy of the message (reliable, unordered etc.) |
-> GameWire m (Event (t (NetworkMessageType i))) (Event ()) |
Encodes a message for specific actor type and send it to remote host, arrow version Note: mid-level API is not safe to use with low-level at same time as first bytes of formed message are used for actor id. So, you need to have a special forbidden id for you custom messages.
peerSendIndexedManyDyn Source #
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, NetworkMessage i, Serialize (NetworkMessageType i), Foldable t) | |
=> ChannelID | Which channel we are sending within |
-> MessageType | Strategy of the message (reliable, unordered etc.) |
-> GameWire m (Event (t (Peer, i, NetworkMessageType i))) (Event ()) |
Encodes a message for specific actor type and send it to remote host, arrow version. Takes peer, id and message as arrow input.
Helpers
Helper to filter output of peerIndexedMessages
Automatic synchronization
The synchronization API is built around Sync
applicative functor. It allows to combine
complex synchronization strategies from reasonable small amount of basic blocks (see noSync
, clientSide
, serverSide
, condSync
, syncReject
).
Synchornization description is considered as complete, when you get 'Sync m i a a' type (FullSync
type synonym).
After that you can use clientSync
and serverSync
at your actors to sync them.
Example of usage of sync API:
data Player = Player { playerId :: !PlayerId , playerPos :: !(V2 Double) , playerSize :: !Double } deriving (Generic) instance NFData Player newtype PlayerId = PlayerId { unPlayerId :: Int } deriving (Eq, Show, Generic) instance NFData PlayerId instance Hashable PlayerId instance Serialize PlayerId -- | Local message type data PlayerMessage = -- | The player was shot by specified player PlayerShotMessage !PlayerId deriving (Typeable, Generic) instance NFData PlayerMessage instance ActorMessage PlayerId where type ActorMessageType PlayerId = PlayerMessage toCounter = unPlayerId fromCounter = PlayerId -- | Remote message type data PlayerNetMessage = NetMsgPlayerFire !(V2 Double) deriving (Generic, Show) instance NFData PlayerNetMessage instance Serialize PlayerNetMessage instance NetworkMessage PlayerId where type NetworkMessageType PlayerId = PlayerNetMessage playerActorServer :: :: (PlayerId -> Player) -> AppActor PlayerId Game Player playerActorServer initialPlayer = makeActor $ i -> stateWire (initialPlayer i) $ mainController i where mainController i = proc (g, p) -> do p2 <- peerProcessIndexedM peer (ChannelID 0) i netProcess -< p forceNF . serverSync playerSync i . playerShot -< p2 where -- | Shortcut for peer peer = playerPeer $ initialPlayer i -- | Handle when player is shot playerShot :: AppWire Player Player playerShot = proc p -> do emsg <- actorMessages i isPlayerShotMessage -< () let newPlayer = p { playerPos = 0 } returnA -< event p (const newPlayer) emsg -- | Process player specific net messages netProcess :: Player -> PlayerNetMessage -> GameMonadT AppMonad Player netProcess p msg = case msg of NetMsgPlayerFire v -> do let d = normalize v v2 a = V2 a a pos = playerPos p + d * v2 (playerSize p * 1.5) vel = d * v2 bulletSpeed putMsgLnM $ "Fire bullet at " <> pack (show pos) <> " with velocity " <> pack (show vel) actors <- calculatePlayersOnLine pos vel forM_ actors . actorSendM actor . PlayerShotMessage . playerId $ p return p playerSync :: FullSync AppMonad PlayerId Player playerSync = Player <$> pure i <*> clientSide peer 0 playerPos <*> clientSide peer 1 playerSize playerActorClient :: Peer -> PlayerId -> AppActor PlayerId Camera Player playerActorClient peer i = makeFixedActor i $ stateWire initialPlayer $ proc (c, p) -> do processFire -< (c, p) liftGameMonad4 renderSquare -< (playerSize p, playerPos p, playerColor p, c) forceNF . clientSync playerSync peer i -< p where initialPlayer = Player { playerId = i , playerPos = 0 , playerColor = V3 1 0 0 , playerRot = 0 , playerSpeed = 0.5 , playerSize = 1 } processFire :: AppWire (Camera, Player) () processFire = proc (c, p) -> do e <- mouseClick ButtonLeft -< () let wpos = cameraToWorld c $ e let edir = (v -> normalize $ v - playerPos p) $ wpos let emsg = NetMsgPlayerFire $ edir peerSendIndexed peer (ChannelID 0) i ReliableMessage -< emsg returnA -< () playerSync :: FullSync AppMonad PlayerId Player playerSync = Player <$> pure i <*> clientSide peer 0 playerPos <*> clientSide peer 1 playerSize
class NetworkMessage i => RemoteActor i a | i -> a, a -> i Source #
API to support automatic synchronization of actors between client and server
type RemoteActorState i :: * Source #
State of remote actor (should be equal a)
type RemoteActorId a :: * Source #
Id of remote actor (should be equal i)
:: (ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, RemoteActor i a) | |
=> Sync m i s a | Sync strategy |
-> Peer | Server connection |
-> i | Actor id |
-> GameWire m s a | Synchronizing of client state |
Perform client side synchronization
:: (MonadFix m, ActorMonad m, SyncMonad m, NetworkMonad m, LoggingMonad m, RemoteActor i a) | |
=> Sync m i s a | Sync strategy |
-> i | Actor id |
-> GameWire m s a | Synchronizing of server state |
Perform server side synchronization
Synchronization primitives
Special monad that keeps info about synchronization logic
between client and server for type a
. Remote collection
uses the description to generate special code to automatic
synchronization of shared actor state.
m
- means underlying game monad, that will be used during synchronization
i
- means actor unique id type
s
- means actor state that is beeing syncing. As soon as you crafted 'Sync i s s' it means you defined full description how to sync actor state.
a
- is actual value type that the
Sync
value is describing synchronization for. As soon as you crafted 'Sync i s s' it means you defined full description how to sync actor state.
type FullSync m i s = Sync m i s s Source #
Type synonim for those Sync DSL programs that defines full synchronization of actor state
:: (s -> a) | Getter of the field |
-> Sync m i s a |
Perphoms no synchronization, the sync primitive returns local value of field
:: (Eq a, Serialize a, RemoteActor i s) | |
=> Peer | Which peer controls the field, sync messages from other peers are not processed |
-> Word64 | Field id, other side actor should define |
-> (s -> a) | Field getter |
-> Sync m i s a |
Declares that state field is client side, i.e. it is produced in client actor
and then sent to server. For peers that are not equal to specified (owner of the field)
the sync behavior acts as serverSide
.
If server side changes the value manually, client is forced to new server side value.
:: (Serialize a, RemoteActor i s) | |
=> Word64 | Field id, other side actor should define |
-> (s -> a) | Field getter |
-> Sync m i s a |
Declares that state field is server side, i.e. it is produced in server actor and then sent to all clients.
Clients cannot change the value manually.
:: Monad m | |
=> GameWire m s (Event b) | Wire that produces events when sync should be done |
-> (s -> a) | Field getter |
-> Sync m i s a | Sub action that should be done when sync event is produced |
-> Sync m i s a |
Makes synchronization appear only when given wire produces an event.
Note: intended to use with serverSide
:: (Serialize a, RemoteActor i s) | |
=> GameWire m (s, a) (Event a) | Fires event when the synced value is invalid, event carries new value that should be placed and sended to remote peer |
-> Word64 | Id of field to resync at remote host when failed |
-> Sync m i s a | Sub action that produces synced values for first argument |
-> Sync m i s a |
There are sometimes net errors or malicios data change in remote actor, the action provides you ability to reject incorrect values and resync remote actor to fallback value.
Note: intended to use with serverSide
Helpers for conditional synchronization
:: Eq a | |
=> (s -> a) | Field getter |
-> GameWire m s (Event a) |
Produces event when given field is changed
:: (Num a, Ord a) | |
=> (s -> a) | Field getter |
-> a | Delta, variation greater than the value is treated as change |
-> GameWire m s (Event a) |
Produces event when given field is changed
Remote collection
newtype RemActorCollId Source #
Unique id space for remote collections actors
Eq RemActorCollId Source # | |
Ord RemActorCollId Source # | |
Show RemActorCollId Source # | |
Generic RemActorCollId Source # | |
NFData RemActorCollId Source # | |
Hashable RemActorCollId Source # | |
ActorMessage RemActorCollId Source # | |
NetworkMessage RemActorCollId Source # | |
type Rep RemActorCollId Source # | |
type ActorMessageType RemActorCollId Source # | |
type NetworkMessageType RemActorCollId Source # | |
remoteActorCollectionServer Source #
:: (MonadFix m, SyncMonad m, LoggingMonad m, ActorMonad m, NetworkMonad m, Eq i, RemoteActor i b, DynCollection c, FilterConstraint c (GameWireIndexed m i a b), FilterConstraint c (Either () (b, i)), Foldable c2, Functor c2) | |
=> c (GameActor m i a b) | Initial set of actors |
-> GameActor m RemActorCollId (a, Event (c (GameActor m i a b)), Event (c2 i)) (c b) |
Server side collection of network actors that are automatically synchronized to remote clients (creation and removing of actors).
- Second wire input is event with new actors to add to the collection
- Third wire input is event with id of actor to delete from the collection
Note: the collection doesn't do synchronization of actor internal state, you should
call clientSync
by yourself.
remoteActorCollectionClient Source #
:: (SyncMonad m, LoggingMonad m, ActorMonad m, NetworkMonad m, Eq i, RemoteActor i b) | |
=> RemActorCollId | Corresponding server collection id |
-> Peer | Server peer |
-> (i -> GameActor m i a b) | How to construct client side actors |
-> GameActor m RemActorCollId a (Seq b) |
Client side collection of network actors that are automatically synchronized to remote clients (creation and removing of actors).
Note: the collection doesn't do synchronization of actor internal state, you should
call serverSync
by yourself.