-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Rollback/replay NetCode for realtime, deterministic, multiplayer games. -- -- A rollback/replay client-server system for realtime multiplayer games. -- The API only requires you to express your game as a pure, -- deterministic function. -- -- See Alpaca.NetCode to get started. -- --

Advantages

-- -- -- --

Disadvantages

-- -- -- --

Disclaimer

-- -- This is an initial release with minimal functionality and still very -- experimental. Use at your own risk. @package alpaca-netcode @version 0.1.0.0 -- | Rollback and replay based game networking module Alpaca.NetCode.Advanced -- | Run a server for a single game. This will block until the game ends, -- specifically when all players have disconnected. runServerWith :: forall input. (Eq input, Flat input) => ServiceName -> Maybe SimNetConditions -> ServerConfig -> input -> IO () -- | Run a server for a single game. This will block until the game ends, -- specifically when all players have disconnected. runServerWith' :: forall input clientAddress. (Eq input, Flat input, Show clientAddress, Ord clientAddress) => (NetMsg input -> clientAddress -> IO ()) -> IO (NetMsg input, clientAddress) -> Maybe SimNetConditions -> ServerConfig -> input -> IO () -- | Configuration options specific to the server. data ServerConfig ServerConfig :: Int -> Float -> ServerConfig -- | Tick rate (ticks per second). Typically 30 or 60. -- Must be the same across all clients and the server. Packet rate and -- hence network bandwidth will scale linearly with this the tick rate. [scTickRate] :: ServerConfig -> Int -- | Seconds of not receiving packets from a client before disconnecting -- that client. [scClientTimeout] :: ServerConfig -> Float -- | Sensible defaults for ServerConfig based on the tick rate. defaultServerConfig :: Int -> ServerConfig -- | Start a client. This blocks until the initial handshake with the -- server is finished. runClientWith :: forall world input. Flat input => HostName -> ServiceName -> Maybe SimNetConditions -> ClientConfig -> input -> world -> (Map PlayerId input -> Tick -> world -> world) -> IO (Client world input) -- | Start a client. This blocks until the initial handshake with the -- server is finished. runClientWith' :: forall world input. Flat input => (NetMsg input -> IO ()) -> IO (NetMsg input) -> Maybe SimNetConditions -> ClientConfig -> input -> world -> (Map PlayerId input -> Tick -> world -> world) -> IO (Client world input) -- | Configuration options specific to clients. data ClientConfig ClientConfig :: Int -> Float -> Int -> Int -> Int -> ClientConfig -- | Tick rate (ticks per second). Typically 30 or 60. -- Must be the same across all clients and the server. Packet rate and -- hence network bandwidth will scale linearly with this the tick rate. [ccTickRate] :: ClientConfig -> Int -- | Add this constant amount of latency (in seconds) to this client's -- inputs. A good value is 0.03 or something between 0 -- and 0.1. May differ between clients. -- -- Too high of a value and the player will get annoyed at the extra input -- latency. On the other hand, a higher value means less miss-predictions -- of other clients. In the extreme case, set to something higher than -- ping, there will be no miss predictions: all clients will receive -- inputs before rendering the corresponding tick. [ccFixedInputLatency] :: ClientConfig -> Float -- | Maximum number of ticks to predict when sampling. -- defaultClientConfig uses ccTickRate / 2. If the client -- is this many ticks behind the current tick, it will simply stop at an -- earlier tick. You may want to scale this value along with the tick -- rate. May differ between clients. [ccMaxPredictionTicks] :: ClientConfig -> Int -- | If the client's latest known authoritative world is this many ticks -- behind the current tick, no prediction will be done at all when -- sampling. defaultClientConfig uses ccTickRate * 3. -- Useful because want to save CPU cycles for catching up with the -- server. You may want to scale this value along with the tick rate. May -- differ between clients. [ccResyncThresholdTicks] :: ClientConfig -> Int -- | When submitting inputs to the server, we also send a copy of -- ccSubmitInputDuplication many recently submitted inputs in -- order to mittigate the effect for dropped packets. -- defaultClientConfig uses 15. [ccSubmitInputDuplication] :: ClientConfig -> Int -- | Sensible defaults for ClientConfig based on the tick rate. defaultClientConfig :: Int -> ClientConfig -- | A Client. You'll generally obtain this via runClient. data Client world input -- | The client's PlayerId clientPlayerId :: Client world input -> PlayerId -- | Sample the current world state. -- -- . First, This will estimate the current tick based on ping and clock -- synchronization with the server. Then, this extrapolates past the -- latest know authoritative world state by assuming no user inputs have -- changed (unless otherwise known e.g. our own player's inputs are -- known). If the client has been stopped, this will return the last -- predicted world. clientSample :: Client world input -> IO world -- | Sample the world state. First, This will estimate the current tick -- based on ping and clock synchronization with the server. Then, the -- world state will be rollback and inputs replayed as necessary. This -- returns: -- -- clientSample' :: Client world input -> IO ([world], world) -- | Set the client's current input. clientSetInput :: Client world input -> input -> IO () -- | Stop the client. clientStop :: Client world input -> IO () -- | Settings for simulating network conditions. Packets in both the send -- and receive directions are randomly dropped or delayed by `simPing/2` -- plus some random duration between `-simJitter` and simJitter. data SimNetConditions SimNetConditions :: Float -> Float -> Float -> SimNetConditions -- | Extra ping (seconds) [simPing] :: SimNetConditions -> Float -- | Extra jitter (seconds). Should be less than simPing. [simJitter] :: SimNetConditions -> Float -- | Package loss (0 = no packet loss, 1 = 100% packet loss). [simPackageLoss] :: SimNetConditions -> Float -- | The game is broken into discrete ticks starting from 0. newtype Tick Tick :: Int64 -> Tick newtype PlayerId PlayerId :: Word8 -> PlayerId [unPlayerId] :: PlayerId -> Word8 data NetMsg input -- | Either a host name e.g., "haskell.org" or a numeric host -- address string consisting of a dotted decimal IPv4 address or an IPv6 -- address e.g., "192.168.0.1". type HostName = String -- | Either a service name e.g., "http" or a numeric port number. type ServiceName = String -- | This module should be all you need to get started writing multiplayer -- games. See Alpaca.NetCode.Advanced for more advanced usage. module Alpaca.NetCode -- | Run a server for a single game. This will block until the game ends, -- specifically when all players have disconnected. runServer :: forall input. (Eq input, Flat input) => ServiceName -> Int -> input -> IO () -- | Start a client. This blocks until the initial handshake with the -- server is finished. You must call clientSetInput on the -- returned client to submit new inputs. -- -- Think of world as shared state between all clients. Alpaca -- NetCode takes care of synchronizing and predicting the world -- state across all clients. Additionally, clock synchronization is done -- with the server and the "current" tick is decided for you when -- sampling with clientSample. -- -- Typical usage looks like this: -- --
--   main :: IO ()
--   main = do
--     myClient <- runClient "localhost" "8111" 30 myInput0 myWorld0 worldStep
--     let myPlayerId = clientPlayerId myClient
--   
--     mainGameLoop $ do
--       myInput <- pollInput          -- Poll inputs from some other library
--       clientSetInput myClient       -- Push inputs to Alpaca NetCode
--       world <- clientSample         -- Sample the current (predicted) world
--       renderWorld myPlayerId world  -- Render the world
--   
--       -- You're free to do IO and maintain state local to the client.
--   
--       return (gameIsOver world)     -- Return True to keep looping
--   
--   clientStop myClient
--   
--   -- Given
--   data World = World { .. }
--   data Input = Input { .. } deriving (Generic, Eq, Flat)
--   myWorld0 :: World
--   gameIsOver :: World -> Bool
--   myInput0 :: Input
--   worldStep :: Map PlayerId Input -> Tick -> World -> World
--   renderWorld :: PlayerId -> World -> IO ()
--   pollInput :: IO Input
--   mainGameLoop :: IO Bool -> IO ()
--   
runClient :: forall world input. Flat input => HostName -> ServiceName -> Int -> input -> world -> (Map PlayerId input -> Tick -> world -> world) -> IO (Client world input) -- | A Client. You'll generally obtain this via runClient. data Client world input -- | The client's PlayerId clientPlayerId :: Client world input -> PlayerId -- | Sample the current world state. -- -- . First, This will estimate the current tick based on ping and clock -- synchronization with the server. Then, this extrapolates past the -- latest know authoritative world state by assuming no user inputs have -- changed (unless otherwise known e.g. our own player's inputs are -- known). If the client has been stopped, this will return the last -- predicted world. clientSample :: Client world input -> IO world -- | Set the client's current input. clientSetInput :: Client world input -> input -> IO () -- | Stop the client. clientStop :: Client world input -> IO () -- | The game is broken into discrete ticks starting from 0. newtype Tick Tick :: Int64 -> Tick newtype PlayerId PlayerId :: Word8 -> PlayerId [unPlayerId] :: PlayerId -> Word8 -- | Either a host name e.g., "haskell.org" or a numeric host -- address string consisting of a dotted decimal IPv4 address or an IPv6 -- address e.g., "192.168.0.1". type HostName = String -- | Either a service name e.g., "http" or a numeric port number. type ServiceName = String