backroll/
lib.rs

1use std::time::Duration;
2use thiserror::Error;
3
4mod backend;
5pub mod command;
6mod input;
7mod protocol;
8mod sync;
9mod time_sync;
10
11pub use backend::*;
12pub use backroll_transport as transport;
13pub use input::GameInput;
14
15/// The maximum number of players supported in a single game.
16pub const MAX_PLAYERS: usize = 8;
17// Approximately 2 seconds of frames.
18const MAX_ROLLBACK_FRAMES: usize = 120;
19
20type Frame = i32;
21const NULL_FRAME: Frame = -1;
22
23fn is_null(frame: Frame) -> bool {
24    frame < 0
25}
26
27/// A handle for a player in a Backroll session.
28#[derive(Copy, Clone, Debug)]
29pub struct PlayerHandle(pub usize);
30
31/// Players within a Backroll session.
32#[derive(Clone)]
33pub enum Player {
34    /// The local player. Backroll currently only supports one local player per machine.
35    Local,
36    /// A remote player that is not on the local session.
37    Remote(transport::Peer),
38}
39
40impl Player {
41    pub(crate) fn is_local(&self) -> bool {
42        matches!(self, Self::Local)
43    }
44}
45
46impl Default for Player {
47    fn default() -> Self {
48        Self::Local
49    }
50}
51
52/// Compile time parameterization for Backroll sessions.
53pub trait Config: 'static {
54    /// The input type for a Backroll session. This is the only game-related data
55    /// transmitted over the network.
56    ///
57    /// Reminder: Types implementing [Pod] may not have the same byte representation
58    /// on platforms with different endianness. Backroll assumes that all players are
59    /// running with the same endianness when encoding and decoding inputs. It may be
60    /// worthwhile to ensure that all players are running with the same endianess.
61    ///
62    /// [Pod]: bytemuck::Pod
63    type Input: PartialEq + bytemuck::Pod + bytemuck::Zeroable + Send + Sync;
64
65    /// The save state type for the session. This type must be safe to send across
66    /// threads and have a 'static lifetime. This type is also responsible for
67    /// dropping any internal linked state via [Drop].
68    ///
69    /// [Drop]: std::ops::Drop
70    type State: Clone + Send + Sync + 'static;
71}
72
73#[derive(Clone, Debug, Error)]
74pub enum BackrollError {
75    #[error("Multiple players ")]
76    MultipleLocalPlayers,
77    #[error("Action cannot be taken while in rollback.")]
78    InRollback,
79    #[error("The session has not been synchronized yet.")]
80    NotSynchronized,
81    #[error("The simulation has reached the prediction barrier.")]
82    ReachedPredictionBarrier,
83    #[error("Invalid player handle: {:?}", .0)]
84    InvalidPlayer(PlayerHandle),
85    #[error("Player already disconnected: {:?}", .0)]
86    PlayerDisconnected(PlayerHandle),
87}
88
89pub type BackrollResult<T> = Result<T, BackrollError>;
90
91#[derive(Clone, Debug, Default)]
92/// Event that occurs during the course of a session.
93pub struct NetworkStats {
94    /// The round time trip duration between the local player and the
95    /// remote.
96    pub ping: Duration,
97    /// The number of outgoing messages currently not sent.
98    pub send_queue_len: usize,
99    /// The number of incoming messages currently not processed.
100    pub recv_queue_len: usize,
101    /// The number of kilobytes sent per second, a rolling average.
102    pub kbps_sent: u32,
103
104    /// The local frame advantage relative to the associated peer.
105    pub local_frames_behind: Frame,
106    /// The remote frame advantage of the associated peer relative to the local player.
107    pub remote_frames_behind: Frame,
108}
109
110#[derive(Clone, Debug)]
111/// Event that occurs during the course of a session.
112pub enum Event {
113    /// A initial response packet from the remote player has been recieved.
114    Connected(PlayerHandle),
115    /// A response from a remote player has been recieved during the initial
116    /// synchronization handshake.
117    Synchronizing {
118        player: PlayerHandle,
119        count: u8,
120        total: u8,
121    },
122    /// The initial synchronization handshake has been completed. The connection
123    /// is considered live now.
124    Synchronized(PlayerHandle),
125    /// All remote peers are now synchronized, the session is can now start
126    /// running.
127    Running,
128    /// The connection with a remote player has been disconnected.
129    Disconnected(PlayerHandle),
130    /// The local client is several frames ahead of all other peers. Might need
131    /// to stall a few frames to allow others to catch up.
132    TimeSync { frames_ahead: u8 },
133    /// The connection with a remote player has been temporarily interrupted.
134    ConnectionInterrupted {
135        player: PlayerHandle,
136        disconnect_timeout: Duration,
137    },
138    /// The connection with a remote player has been resumed after being interrupted.
139    ConnectionResumed(PlayerHandle),
140}