Safe Haskell | None |
---|---|
Language | Haskell2010 |
This module should be all you need to get started writing multiplayer games. See Alpaca.NetCode.Advanced for more advanced usage.
Synopsis
- runServer :: forall input. (Eq input, Flat input) => ServiceName -> Int -> input -> IO ()
- runClient :: forall world input. Flat input => HostName -> ServiceName -> Int -> input -> world -> (Map PlayerId input -> Tick -> world -> world) -> IO (Client world input)
- data Client world input
- clientPlayerId :: Client world input -> PlayerId
- clientSample :: Client world input -> IO world
- clientSetInput :: Client world input -> input -> IO ()
- clientStop :: Client world input -> IO ()
- newtype Tick = Tick Int64
- newtype PlayerId = PlayerId {
- unPlayerId :: Word8
- type HostName = String
- type ServiceName = String
Documentation
:: forall input. (Eq input, Flat input) | |
=> ServiceName | The server's port number e.g. |
-> Int | Tick rate (ticks per second). Typically |
-> input | Initial input for new players. Must be the same across all clients and the server. |
-> IO () |
Run a server for a single game. This will block until the game ends, specifically when all players have disconnected.
:: forall world input. Flat input | |
=> HostName | The server's host name or IP address e.g. |
-> ServiceName | The server's port number e.g. |
-> Int | Tick rate (ticks per second). Typically |
-> input | Initial input for new players. Must be the same across all clients and the server. Note that the client and server do input "prediction" by assuming |
-> world | Initial world state. Must be the same across all clients. |
-> (Map PlayerId input -> Tick -> world -> world) | A deterministic stepping function (for a single tick). In practice you
can choose to use whatever monad stack within as long as you (un)wrap into
a pure function e.g. you can use
It is important that this is deterministic else clients' states will diverge. Beware of floating point non-determinism! |
-> IO (Client world input) |
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 ()
clientPlayerId :: Client world input -> PlayerId Source #
The client's PlayerId
clientSample :: Client world input -> IO world Source #
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.
clientSetInput :: Client world input -> input -> IO () Source #
Set the client's current input.
clientStop :: Client world input -> IO () Source #
Stop the client.
Types
The game is broken into discrete ticks starting from 0.
Instances
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 ServiceName = String #
Either a service name e.g., "http"
or a numeric port number.