### start_p2p_session Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Consumes the builder to construct a P2PSession and starts synchronization of endpoints. ```APIDOC ## start_p2p_session ### Description Consumes the builder to construct a `P2PSession` and starts synchronization of endpoints. ### Method `start_p2p_session(self, socket: impl NonBlockingSocket + 'static) -> Result, GgrsError>` ### Errors * Returns `InvalidRequest` if insufficient players have been registered. ``` -------------------------------- ### WASM / WebRTC Example Implementation Source: https://docs.rs/ggrs/0.12.0/ggrs/trait.NonBlockingSocket.html An example of implementing the NonBlockingSocket trait for use with WebRTC data channels via the matchbox_socket library on WASM. ```APIDOC ## WASM / WebRTC example On WASM, UDP sockets are unavailable. A common approach is to use matchbox_socket which wraps WebRTC data channels with a UDP-like interface. The implementation pattern looks like this: ```rust use ggrs::{Message, NonBlockingSocket}; use matchbox_socket::{PeerId, WebRtcSocket}; pub struct MatchboxSocket(WebRtcSocket); impl NonBlockingSocket for MatchboxSocket { fn send_to(&mut self, msg: &Message, addr: &PeerId) { let encoded = bincode::serialize(msg).expect("serialization failed"); self.0.send(encoded.into(), *addr); } fn receive_all_messages(&mut self) -> Vec<(PeerId, Message)> { self.0.receive() .filter_map(|(peer, packet)| { let msg = bincode::deserialize(&packet).ok()?; Some((peer, msg)) }) .collect() } } ``` Then use `PeerId` as `Config::Address` and pass your `MatchboxSocket` to `SessionBuilder::start_p2p_session()`. ``` -------------------------------- ### start_p2p_session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Consumes the builder to construct a [`P2PSession`] and starts synchronization of endpoints. Returns [`InvalidRequest`] if insufficient players have been registered. ```APIDOC ## start_p2p_session ### Description Constructs and starts a P2P session using the configured builder settings. It also initiates the synchronization process for all connected endpoints. ### Method `start_p2p_session` ### Parameters - **socket** (impl NonBlockingSocket + 'static) - The non-blocking socket to use for P2P communication. ### Errors - `GgrsError::InvalidRequest`: If the number of registered players is insufficient. ``` -------------------------------- ### Start P2P Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Constructs a `P2PSession` and initiates endpoint synchronization. This method requires a `NonBlockingSocket` and returns an error if an insufficient number of players have been registered. ```rust pub fn start_p2p_session( mut self, socket: impl NonBlockingSocket + 'static, ) -> Result, GgrsError> { // check if all players are added for player_handle in 0..self.num_players { ``` -------------------------------- ### Initialize GGRS SessionBuilder Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Constructs a new SessionBuilder with all default values. Use this as a starting point for configuring a new GGRS session. ```rust pub fn new() -> Self { Self { player_reg: PlayerRegistry::new(), local_players: 0, num_players: DEFAULT_PLAYERS, max_prediction: DEFAULT_MAX_PREDICTION_FRAMES, fps: DEFAULT_FPS, sparse_saving: DEFAULT_SAVE_MODE, desync_detection: DEFAULT_DETECTION_MODE, disconnect_timeout: DEFAULT_DISCONNECT_TIMEOUT, disconnect_notify_start: DEFAULT_DISCONNECT_NOTIFY_START, input_delay: DEFAULT_INPUT_DELAY, check_dist: DEFAULT_CHECK_DISTANCE, max_frames_behind: DEFAULT_MAX_FRAMES_BEHIND, catchup_speed: DEFAULT_CATCHUP_SPEED, } } ``` -------------------------------- ### Build a P2P GGRS Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/lib.rs.html Construct a GGRS session using `SessionBuilder`. This example shows how to configure the number of players, input delay, and add local and remote players for a peer-to-peer connection. ```rust use ggrs::{SessionBuilder, PlayerType, UdpNonBlockingSocket}; struct GgrsConfig; impl ggrs::Config for GgrsConfig { type Input = u8; type InputPredictor = ggrs::PredictRepeatLast; type State = u8; type Address = std::net::SocketAddr; } let socket = UdpNonBlockingSocket::bind_to_port(7000).unwrap(); let mut session = SessionBuilder::::new() .with_num_players(2).unwrap() .with_input_delay(2) .add_player(PlayerType::Local, 0).unwrap() .add_player(PlayerType::Remote("127.0.0.1:7001".parse().unwrap()), 1).unwrap() .start_p2p_session(socket).unwrap(); ``` -------------------------------- ### Build a P2P Session Source: https://docs.rs/ggrs/0.12.0/ggrs/index.html Constructs a new peer-to-peer session using `SessionBuilder`. This example sets up a session for two players with a specified input delay and adds local and remote players. ```rust let socket = UdpNonBlockingSocket::bind_to_port(7000).unwrap(); let mut session = SessionBuilder::::new() .with_num_players(2).unwrap() .with_input_delay(2) .add_player(PlayerType::Local, 0).unwrap() .add_player(PlayerType::Remote("127.0.0.1:7001".parse().unwrap()), 1).unwrap() .start_p2p_session(socket).unwrap(); ``` -------------------------------- ### SessionBuilder::add_player Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Adds a player to the session. This must be called for each player before starting the session. ```APIDOC ## add_player ### Description Must be called for each player in the session (e.g. in a 3 player session, must be called 3 times) before starting the session. Player handles for players should be between 0 and `num_players`, spectator handles should be higher than `num_players`. Later, you will need the player handle to add input, change parameters or disconnect the player or spectator. ### Method `add_player(player_type: PlayerType, player_handle: PlayerHandle)` ### Parameters * `player_type` (PlayerType) - The type of player (e.g., local, remote). * `player_handle` (PlayerHandle) - The unique handle for the player. ### Returns `Result` - The updated SessionBuilder or an error if the handle is invalid or already exists. ### Errors * Returns `InvalidRequest` if a player with that handle has been added before * Returns `InvalidRequest` if the handle is invalid for the given `PlayerType` ``` -------------------------------- ### Get Handles by Address Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Retrieves all player handles associated with a specific network address. ```rust pub fn handles_by_address(&self, addr: T::Address) -> Vec ``` -------------------------------- ### Get Number of Players in P2P Spectator Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Returns the total number of players the session was initialized with. ```rust /// Returns the number of players this session was constructed with. pub fn num_players(&self) -> usize { self.num_players } ``` -------------------------------- ### Example Usage of GameStateCell::data() Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sync_layer.rs.html Demonstrates how to use GameStateCell::data() to access game state without cloning, particularly useful when the game state is not Clone. ```rust # use ggrs::{Frame, GameStateCell}; // Setup normally performed by GGRS behind the scenes let mut cell = GameStateCell::::default(); let frame_num: Frame = 0; // The state of our example game will be just a String, and our game state isn't Clone struct MyGameState { player_name: String }; // Setup you do when GGRS requests you to save game state { let game_state = MyGameState { player_name: "alex".to_owned() }; let checksum = None; // (in real usage, save a checksum! We omit it here because it's not // relevant to this example) cell.save(frame_num, Some(game_state), checksum); } // We can't use load() to access the game state, because it's not Clone // println!("{}", cell.load().player_name); // compile error: Clone bound not satisfied // But we can still read the game state without cloning: let game_state_accessor = cell.data().expect("should have a gamestate stored"); assert_eq!(game_state_accessor.player_name, "alex"); ``` -------------------------------- ### Start SyncTest Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Initializes a SyncTestSession for debugging determinism issues. This session simulates rollbacks and compares checksums without network involvement. Sparse saving is not compatible with this mode. ```rust pub fn start_synctest_session(self) -> Result, GgrsError> { if self.check_dist >= self.max_prediction { return Err(GgrsError::InvalidRequest { info: "Check distance too big.".to_owned(), }); } if self.sparse_saving { return Err(GgrsError::InvalidRequest { info: "Sparse saving is not supported for synctest sessions.".to_owned(), }); } Ok(SyncTestSession::new( self.num_players, self.max_prediction, self.check_dist, self.input_delay, )) } ``` -------------------------------- ### Start Spectator Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Creates a SpectatorSession for observing a GGRS game. It connects to a specified host address and uses a provided socket. The host broadcasts inputs to this session. ```rust pub fn start_spectator_session( self, host_addr: T::Address, socket: impl NonBlockingSocket + 'static, ) -> SpectatorSession { // create host endpoint let mut host = UdpProtocol::new( (0..self.num_players).collect(), host_addr, self.num_players, 1, //should not matter since the spectator is never sending self.max_prediction, self.disconnect_timeout, self.disconnect_notify_start, self.fps, DesyncDetection::Off, ); host.synchronize(); SpectatorSession::new( self.num_players, Box::new(socket), host, self.max_frames_behind, self.catchup_speed, ) } ``` -------------------------------- ### Get Number of Players Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html Returns the total number of players the spectator session was initialized with. ```rust pub fn num_players(&self) -> usize ``` -------------------------------- ### Get Local Player Handles Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns a vector containing the handles of all local players added to the session. ```rust pub fn local_player_handles(&self) -> Vec ``` -------------------------------- ### Get Spectator Handles Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Retrieves a list of all spectator handles. Use this to identify connected spectators. ```rust pub(crate) fn spectator_handles(&self) -> Vec { self.handles .iter() .filter_map(|(k, v)| match v { PlayerType::Spectator(_) => Some(*k), PlayerType::Local | PlayerType::Remote(_) => None, }) .collect() } ``` -------------------------------- ### Get Frames Ahead Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Estimates and returns the number of frames the current session is ahead of other connected sessions. ```rust pub fn frames_ahead(&self) -> i32 ``` -------------------------------- ### Availability and Usage Example Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.NetworkStats.html Network statistics are computed over a rolling 1-second window. Until at least one second has elapsed since the session entered the `Running` state, methods like `network_stats()` return `GgrsError::NotEnoughData`. It's recommended to poll for stats in your game loop and discard the error until data is available. ```APIDOC ## §Availability Stats are computed over a rolling 1-second window. Until at least one second has elapsed since the session entered the `Running` state, those methods return `GgrsError::NotEnoughData` rather than a `NetworkStats` value. Poll in your game loop after synchronization and discard the error until data is available: ```rust match session.network_stats(player_handle) { Ok(stats) => { /* use stats */ } // Stats are available Err(GgrsError::NotEnoughData) => { /* still warming up — ignore */ } // Not enough data yet Err(e) => return Err(e), // Handle other potential errors } ``` ``` -------------------------------- ### Get Remote Player Handles Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns a vector containing the handles of all remote players currently in the session. ```rust pub fn remote_player_handles(&self) -> Vec ``` -------------------------------- ### Add a player to the session Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Registers a player or spectator for the session. This must be called for each participant before starting the session. Player handles must be unique and within the valid range determined by `with_num_players`. ```rust pub fn add_player( self, player_type: PlayerType, player_handle: PlayerHandle, ) -> Result ``` -------------------------------- ### Get Inputs at Frame in P2P Spectator Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Retrieves synchronized inputs for a specific frame. Handles cases where inputs are not yet available or have been lost due to buffer overflow. ```rust fn inputs_at_frame( &self, frame_to_grab: Frame, ) -> Result, GgrsError> { let player_inputs = &self.inputs[frame_to_grab as usize % SPECTATOR_BUFFER_SIZE]; // We haven't received the input from the host yet. Wait. if player_inputs[0].frame < frame_to_grab { return Err(GgrsError::PredictionThreshold); } // The host is more than [`SPECTATOR_BUFFER_SIZE`] frames ahead of the spectator. The input we need is gone forever. if player_inputs[0].frame > frame_to_grab { return Err(GgrsError::SpectatorTooFarBehind); } Ok(player_inputs .iter() .enumerate() .map(|(handle, player_input)| { if self.host_connect_status[handle].disconnected && self.host_connect_status[handle].last_frame < frame_to_grab { (player_input.input, InputStatus::Disconnected) } else { (player_input.input, InputStatus::Confirmed) } }) .collect()) } ``` -------------------------------- ### P2PSession::new Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Creates a new P2PSession. This involves setting up the synchronization layer, player registry, and initial session state based on the provided parameters. ```APIDOC ## P2PSession::new ### Description Creates a new [`P2PSession`] for players who participate on the game input. After creating the session, add local and remote players, set input delay for local players and then start the session. The session will use the provided socket. ### Parameters - `num_players` (usize) - The total number of players in the session. - `max_prediction` (usize) - The maximum frame prediction. - `socket` (Box>) - The non-blocking socket for network communication. - `players` (PlayerRegistry) - The registry of players, including local and remote participants. - `sparse_saving` (bool) - Whether to enable sparse saving of game states. - `desync_detection` (DesyncDetection) - Configuration for desync detection. - `input_delay` (usize) - The input delay for local players. ``` -------------------------------- ### Fetch Network Statistics Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html Fetches statistics about the network connection quality for the spectator session. Returns an error if the endpoint has not started connecting or if less than one second has elapsed since connection. ```rust pub fn network_stats(&self) -> Result ``` -------------------------------- ### Add Player to SessionBuilder Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Adds a player to the session configuration. This method must be called for each player before starting the session. It validates player handles and types, returning an error if the handle is already in use or invalid for the specified player type. ```rust pub fn add_player( mut self, player_type: PlayerType, player_handle: PlayerHandle, ) -> Result { // check if the player handle is already in use if self.player_reg.handles.contains_key(&player_handle) { return Err(GgrsError::InvalidRequest { info: "Player handle already in use.".to_owned(), }); } // check if the player handle is valid for the given player type match player_type { PlayerType::Local => { self.local_players += 1; if player_handle >= self.num_players { return Err(GgrsError::InvalidRequest { info: "The player handle you provided is invalid. For a local player, the handle should be between 0 and num_players".to_owned(), ``` -------------------------------- ### Advance frame and get requests Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Notifies GGRS that the game state is ready to advance by one frame. Returns a `Vec` that must be fulfilled in the exact order provided to avoid panics. Errors if the session is not ready to accept input. ```rust pub fn advance_frame(&mut self) -> Result>, GgrsError> { ``` -------------------------------- ### Get Spectator Handles Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns a vector containing the handles of all registered spectators. ```rust pub fn spectator_handles(&self) -> Vec ``` -------------------------------- ### UdpProtocol Constructor Source: https://docs.rs/ggrs/0.12.0/src/ggrs/network/protocol.rs.html Initializes a new UdpProtocol instance with provided configuration parameters. It sets up player handles, connection status, input history, and network-related constants. ```rust impl UdpProtocol { #[allow(clippy::too_many_arguments)] pub(crate) fn new( mut handles: Vec, peer_addr: T::Address, num_players: usize, local_players: usize, max_prediction: usize, disconnect_timeout: Duration, disconnect_notify_start: Duration, fps: usize, desync_detection: DesyncDetection, ) -> Self { let mut magic = rand::random::(); while magic == 0 { magic = rand::random::(); } handles.sort_unstable(); let recv_player_num = handles.len(); // peer connection status let mut peer_connect_status = Vec::new(); for _ in 0..num_players { peer_connect_status.push(ConnectionStatus::default()); } // received input history let mut recv_inputs = HashMap::new(); recv_inputs.insert(NULL_FRAME, InputBytes::zeroed::(recv_player_num)); Self { num_players, handles, send_queue: VecDeque::new(), event_queue: VecDeque::new(), // state state: ProtocolState::Initializing, sync_remaining_roundtrips: NUM_SYNC_PACKETS, sync_random_requests: HashSet::new(), running_last_quality_report: Instant::now(), running_last_input_recv: Instant::now(), disconnect_notify_sent: false, disconnect_event_sent: false, // constants disconnect_timeout, disconnect_notify_start, shutdown_timeout: Instant::now(), fps, magic, // the other client peer_addr, remote_magic: 0, peer_connect_status, // input compression pending_output: VecDeque::with_capacity(PENDING_OUTPUT_SIZE), last_acked_input: InputBytes::zeroed::(local_players), max_prediction, recv_inputs, // time sync time_sync_layer: TimeSync::new(), local_frame_advantage: 0, remote_frame_advantage: 0, // network stats_start_time: 0, } } } ``` -------------------------------- ### Get Number of Spectators Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns the current number of registered spectators in the session. ```rust pub fn num_spectators(&self) -> usize ``` -------------------------------- ### Implement NonBlockingSocket for WebRTC with matchbox_socket Source: https://docs.rs/ggrs/0.12.0/ggrs/trait.NonBlockingSocket.html Example implementation of the NonBlockingSocket trait for use with WebRTC data channels via the matchbox_socket library on WASM. This allows GGRS to use WebRTC as a transport layer. ```rust use ggrs::{Message, NonBlockingSocket}; use matchbox_socket::{PeerId, WebRtcSocket}; pub struct MatchboxSocket(WebRtcSocket); impl NonBlockingSocket for MatchboxSocket { fn send_to(&mut self, msg: &Message, addr: &PeerId) { let encoded = bincode::serialize(msg).expect("serialization failed"); self.0.send(encoded.into(), *addr); } fn receive_all_messages(&mut self) -> Vec<(PeerId, Message)> { self.0.receive() .filter_map(|(peer, packet)| { let msg = bincode::deserialize(&packet).ok()?; Some((peer, msg)) }) .collect() } } ``` -------------------------------- ### Get Current Frame Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html Returns the current frame number of the spectator session. ```rust pub fn current_frame(&self) -> Frame ``` -------------------------------- ### Get Current Frame in SyncLayer Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sync_layer.rs.html Returns the current frame number being processed by the `SyncLayer`. ```rust pub(crate) fn current_frame(&self) -> Frame { self.current_frame } ``` -------------------------------- ### start_synctest_session Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Consumes the builder to construct a new SyncTestSession for detecting non-determinism bugs. ```APIDOC ## start_synctest_session ### Description Consumes the builder to construct a new `SyncTestSession`. During a `SyncTestSession`, GGRS simulates a rollback every frame and re-simulates the last `check_distance` frames, then compares checksums against the originals. A mismatch indicates a non-determinism bug in your save/load/advance logic. No network is involved. Checksum comparisons require a `check_distance` of 2 or higher (the default). ### Method `start_synctest_session(self) -> Result, GgrsError>` ### Errors * Returns `InvalidRequest` if `check_distance` is greater than or equal to `max_prediction_window`. * Returns `InvalidRequest` if sparse saving is enabled — synctest must save every frame to compare checksums across the full check window, so sparse saving is incompatible. ``` -------------------------------- ### Initialize Input Queues in SyncLayer Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sync_layer.rs.html Initializes input queues for each player when creating a new SyncLayer. Each queue is created with `InputQueue::new()`. ```rust let mut input_queues = Vec::new(); for _ in 0..num_players { input_queues.push(InputQueue::new()); } ``` -------------------------------- ### Get Confirmed Frame Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns the highest frame for which all player inputs have been received and confirmed. ```rust pub fn confirmed_frame(&self) -> Frame ``` -------------------------------- ### Initialize SyncTestSession Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/sync_test_session.rs.html Creates a new SyncTestSession with specified parameters for number of players, prediction window, check distance, and input delay. ```rust pub(crate) fn new( num_players: usize, max_prediction: usize, check_distance: usize, input_delay: usize, ) -> Self { let mut dummy_connect_status = Vec::new(); for _ in 0..num_players { dummy_connect_status.push(ConnectionStatus::default()); } let mut sync_layer = SyncLayer::new(num_players, max_prediction); for i in 0..num_players { sync_layer.set_frame_delay(i, input_delay); } Self { num_players, max_prediction, check_distance, sync_layer, dummy_connect_status, checksum_history: HashMap::new(), local_inputs: HashMap::new(), } } ``` -------------------------------- ### UdpProtocol::new Source: https://docs.rs/ggrs/0.12.0/src/ggrs/network/protocol.rs.html Constructs a new UdpProtocol instance with the specified configuration. ```APIDOC pub(crate) fn new( mut handles: Vec, peer_addr: T::Address, num_players: usize, local_players: usize, max_prediction: usize, disconnect_timeout: Duration, disconnect_notify_start: Duration, fps: usize, desync_detection: DesyncDetection, ) -> Self ``` -------------------------------- ### Get Type ID Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.UdpNonBlockingSocket.html Returns the TypeId of the `UdpNonBlockingSocket` instance. This is part of the Any trait implementation. ```rust fn type_id(&self) -> TypeId ``` -------------------------------- ### Get Number of Spectators Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Returns the total count of spectators. Excludes local and remote players. ```rust pub(crate) fn num_spectators(&self) -> usize { self.handles .iter() .filter(|(_, v)| matches!(v, PlayerType::Spectator(_))) .count() } ``` -------------------------------- ### Create a new P2P Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Initializes a new P2P session with specified parameters including player count, prediction, socket, player registry, saving strategy, desync detection, and input delay. It configures the sync layer and sets the initial session state. ```rust pub(crate) fn new( num_players: usize, max_prediction: usize, socket: Box>, players: PlayerRegistry, sparse_saving: bool, desync_detection: DesyncDetection, input_delay: usize, ) -> Self { // local connection status let mut local_connect_status = Vec::new(); for _ in 0..num_players { local_connect_status.push(ConnectionStatus::default()); } // sync layer & set input delay let mut sync_layer = SyncLayer::new(num_players, max_prediction); for (player_handle, player_type) in &players.handles { if matches!(player_type, PlayerType::Local) { sync_layer.set_frame_delay(*player_handle, input_delay); } } // initial session state - if there are no endpoints, we don't need a synchronization phase let state = if players.remotes.len() + players.spectators.len() == 0 { SessionState::Running } else { SessionState::Synchronizing }; let sparse_saving = if max_prediction == 0 && sparse_saving { // in lockstep mode, saving will never happen, but we use the last saved frame to mark // control marking frames confirmed, so we need to turn off sparse saving to ensure that // frames are marked as confirmed - otherwise we will never advance the game state. warn!( "Sparse saving setting is ignored because lockstep mode is on " "(max_prediction set to 0), so no saving will take place" ); false } else { sparse_saving }; Self { state, num_players, max_prediction, sparse_saving, socket, local_connect_status, next_recommended_sleep: 0, next_spectator_frame: 0, frames_ahead: 0, sync_layer, disconnect_frame: NULL_FRAME, player_reg: players, event_queue: VecDeque::new(), local_inputs: HashMap::new(), desync_detection, local_checksum_history: HashMap::new(), last_sent_checksum_frame: NULL_FRAME, } } ``` -------------------------------- ### InputQueue::new Source: https://docs.rs/ggrs/0.12.0/src/ggrs/input_queue.rs.html Creates a new, empty InputQueue. ```APIDOC ## InputQueue::new ### Description Creates a new instance of `InputQueue` with default values. ### Method `InputQueue::new()` ### Returns A new `InputQueue` instance. ``` -------------------------------- ### Get Number of Players Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Returns the total count of local and remote players. Excludes spectators. ```rust pub(crate) fn num_players(&self) -> usize { self.handles .iter() .filter(|(_, v)| matches!(v, PlayerType::Local | PlayerType::Remote(_))) .count() } ``` -------------------------------- ### start_synctest_session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Consumes the builder to construct a new SyncTestSession. This session is used for testing determinism by simulating rollbacks and comparing checksums without network involvement. ```APIDOC ## start_synctest_session ### Description Consumes the builder to construct a new [`SyncTestSession`]. During a [`SyncTestSession`], GGRS simulates a rollback every frame and re-simulates the last `check_distance` frames, then compares checksums against the originals. A mismatch indicates a non-determinism bug in your save/load/advance logic. No network is involved. Checksum comparisons require a `check_distance` of 2 or higher (the default). ### Method ```rust pub fn start_synctest_session(self) -> Result, GgrsError> ``` ### Errors - Returns [`InvalidRequest`] if `check_distance` is greater than or equal to `max_prediction`. - Returns [`InvalidRequest`] if sparse saving is enabled — synctest must save every frame to compare checksums across the full check window, so sparse saving is incompatible. [`InvalidRequest`]: GgrsError::InvalidRequest ``` -------------------------------- ### start_spectator_session Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Consumes the builder to create a new SpectatorSession, allowing spectating without contributing input. ```APIDOC ## start_spectator_session ### Description Consumes the builder to create a new `SpectatorSession`. A `SpectatorSession` provides all functionality to connect to a remote host in a peer-to-peer fashion. The host will broadcast all confirmed inputs to this session. This session can be used to spectate a session without contributing to the game input. ### Method `start_spectator_session(self, host_addr: T::Address, socket: impl NonBlockingSocket + 'static) -> SpectatorSession` ``` -------------------------------- ### Get Current Frame in P2P Spectator Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Returns the current frame number of the GGRS session. ```rust /// Returns the current frame of a session. pub fn current_frame(&self) -> Frame { self.current_frame } ``` -------------------------------- ### Get Desync Detection Mode Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.P2PSession.html Returns the desynchronization detection mode configured for this session upon creation. ```rust pub fn desync_detection(&self) -> DesyncDetection ``` -------------------------------- ### SyncTestSession::new Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/sync_test_session.rs.html Constructs a new SyncTestSession. Initializes the session with the specified number of players, prediction window, check distance, and input delay. ```APIDOC ## SyncTestSession::new ### Description Constructs a new `SyncTestSession`. ### Parameters - `num_players` (usize): The total number of players in the session. - `max_prediction` (usize): The maximum number of frames to predict ahead. - `check_distance` (usize): The number of frames to roll back and re-simulate for checksum verification. - `input_delay` (usize): The input delay for each player. ``` -------------------------------- ### Create P2PSession from Builder Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Constructs a P2PSession by consuming the builder and its configurations. Requires a socket for network communication. ```rust Ok(P2PSession::::new( self.num_players, self.max_prediction, Box::new(socket), self.player_reg, self.sparse_saving, self.desync_detection, self.input_delay, )) } ``` -------------------------------- ### PlayerRegistry Initialization Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Creates a new, empty PlayerRegistry. Use this when initializing a new GGRS session. ```rust pub(crate) fn new() -> Self { Self { handles: HashMap::new(), remotes: HashMap::new(), spectators: HashMap::new(), } } ``` -------------------------------- ### Get Max Prediction Window of SyncTestSession Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SyncTestSession.html Returns the maximum prediction window size configured for the session. ```rust pub fn max_prediction(&self) -> usize ``` -------------------------------- ### Create New SyncLayer Instance Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sync_layer.rs.html Constructs a new `SyncLayer` with specified player count and maximum prediction. Initializes game state, frame counters, and input queues. ```rust pub(crate) fn new(num_players: usize, max_prediction: usize) -> Self { // initialize input_queues let mut input_queues = Vec::new(); for _ in 0..num_players { input_queues.push(InputQueue::new()); } Self { num_players, max_prediction, last_confirmed_frame: NULL_FRAME, last_saved_frame: NULL_FRAME, current_frame: 0, saved_states: SavedStates::new(max_prediction), input_queues, } } ``` -------------------------------- ### Get Check Distance of SyncTestSession Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SyncTestSession.html Returns the check distance set during session creation, which is the length of the simulated rollbacks. ```rust pub fn check_distance(&self) -> usize ``` -------------------------------- ### Get Number of Players in GGRS Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/sync_test_session.rs.html Returns the total number of players the GGRS session was initialized with. This is a read-only property. ```rust pub fn num_players(&self) -> usize { self.num_players } ``` -------------------------------- ### SessionBuilder::new Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SessionBuilder.html Constructs a new SessionBuilder with all default values. ```APIDOC ## new ### Description Construct a new builder with all values set to their defaults. ### Method `new()` ### Returns `Self` - A new instance of SessionBuilder. ``` -------------------------------- ### Get Current Frame of GGRS Session Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/sync_test_session.rs.html Retrieves the current frame number of the GGRS session. This is a simple getter method. ```rust pub fn current_frame(&self) -> Frame { self.sync_layer.current_frame() } ``` -------------------------------- ### Initialize InputQueue Source: https://docs.rs/ggrs/0.12.0/src/ggrs/input_queue.rs.html Creates a new InputQueue with default values. Used for setting up input management. ```rust pub(crate) fn new() -> Self { Self { head: 0, tail: 0, length: 0, frame_delay: 0, first_frame: true, last_added_frame: NULL_FRAME, first_incorrect_frame: NULL_FRAME, last_requested_frame: NULL_FRAME, prediction: PlayerInput::blank_input(NULL_FRAME), inputs: vec![PlayerInput::blank_input(NULL_FRAME); INPUT_QUEUE_LENGTH], } } ``` -------------------------------- ### Get First Incorrect Frame Source: https://docs.rs/ggrs/0.12.0/src/ggrs/input_queue.rs.html Retrieves the frame number of the first incorrect prediction. Useful for debugging synchronization issues. ```rust pub(crate) fn first_incorrect_frame(&self) -> Frame { self.first_incorrect_frame } ``` -------------------------------- ### Get Current Session State Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html Returns the current `SessionState` of a spectator session. This indicates the overall status of the GGRS session. ```rust pub fn current_state(&self) -> SessionState ``` -------------------------------- ### PlayerInput Utility Methods Source: https://docs.rs/ggrs/0.12.0/src/ggrs/frame_info.rs.html Provides utility methods for creating new `PlayerInput` instances, creating blank inputs for a given frame, and comparing inputs. ```rust impl PlayerInput { pub(crate) fn new(frame: Frame, input: I) -> Self { Self { frame, input } } pub(crate) fn blank_input(frame: Frame) -> Self { Self { frame, input: I::default(), } } /// Returns true if only the input data matches, ignoring frame numbers. pub(crate) fn input_matches(&self, other: &Self) -> bool { self.input == other.input } /// Returns true if both the frame number and input data match. #[cfg(test)] pub(crate) fn matches_full(&self, other: &Self) -> bool { self.frame == other.frame && self.input == other.input } } ``` -------------------------------- ### NetworkStats::new() Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.NetworkStats.html Creates a new `NetworkStats` instance with default values. ```APIDOC ## impl NetworkStats ### pub fn new() -> Self Creates a new `NetworkStats` instance with default values. ``` -------------------------------- ### Get SpectatorSession current state Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Returns the current state of the spectator session. This is useful for understanding if the session is synchronizing, running, or has ended. ```rust pub fn current_state(&self) -> SessionState { self.state } ``` -------------------------------- ### Get Confirmed Input Source: https://docs.rs/ggrs/0.12.0/src/ggrs/input_queue.rs.html Retrieves a confirmed player input for a specific frame. Panics if the input is not confirmed, ensuring data integrity. ```rust pub(crate) fn confirmed_input(&self, requested_frame: Frame) -> PlayerInput { let offset = requested_frame as usize % INPUT_QUEUE_LENGTH; if self.inputs[offset].frame == requested_frame { return self.inputs[offset]; } // the requested confirmed input should not be before a prediction. We should not have asked for a known incorrect frame. panic!("SyncLayer::confirmed_input(): There is no confirmed input for the requested frame"); } ``` -------------------------------- ### Test New PlayerInput Creation Source: https://docs.rs/ggrs/0.12.0/src/ggrs/frame_info.rs.html Verifies that the `new` method correctly initializes a `PlayerInput` with the provided frame number and input data. ```rust #[test] fn test_new_stores_frame_and_input() { let input = PlayerInput::new(42, TestInput { inp: 99 }); assert_eq!(input.frame, 42); assert_eq!(input.input.inp, 99); } ``` -------------------------------- ### NetworkStats::new() Constructor Source: https://docs.rs/ggrs/0.12.0/src/ggrs/network/network_stats.rs.html Provides a convenient way to create a new NetworkStats instance with default values. ```rust pub fn new() -> Self { Self::default() } ``` -------------------------------- ### Get Player Handles by Address Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Finds all player handles associated with a specific network address. Useful for managing connections from a single endpoint. ```rust pub fn handles_by_address(&self, addr: T::Address) -> Vec { self.handles .iter() .filter_map(|(h, player_type)| match player_type { PlayerType::Remote(a) | PlayerType::Spectator(a) => Some((h, a)), PlayerType::Local => None, }) .filter_map(|(h, a)| if addr == *a { Some(*h) } else { None }) .collect() } ``` -------------------------------- ### SpectatorSession Methods Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html This section details the methods available on the SpectatorSession struct, allowing users to interact with and query the spectator session's state and network information. ```APIDOC ## SpectatorSession Connects to a remote host in a peer-to-peer fashion without contributing input. The host will broadcast all confirmed inputs to this session, allowing it to replay the game as a spectator. ### Methods #### `current_state(&self) -> SessionState` Returns the current `SessionState` of a session. #### `frames_behind_host(&self) -> usize` Returns how many confirmed frames the spectator has yet to process. Both `last_recv_frame` and `current_frame` are initialised to `NULL_FRAME` (-1), so this returns `0` before any input has arrived from the host, which is correct: neither side has advanced yet. Once the session is running, the invariant `last_recv_frame >= current_frame` is maintained by the session, so the result is always non-negative. #### `network_stats(&self) -> Result` Used to fetch some statistics about the quality of the network connection. ##### Errors * Returns `NotSynchronized` if the endpoint has not yet started connecting. * Returns `NotEnoughData` if less than one second has elapsed since the connection was established. The session may already be `Running`; retry after a short delay. #### `events(&mut self) -> Drain<'_, GgrsEvent>` Returns all events that happened since last queried for events. If the number of stored events exceeds `MAX_EVENT_QUEUE_SIZE`, the oldest events will be discarded. #### `advance_frame(&mut self) -> Result>, GgrsError>` You should call this to notify GGRS that you are ready to advance your gamestate by a single frame. Returns an order-sensitive `Vec`. You should fulfill all requests in the exact order they are provided. Failure to do so will cause panics later. ##### Errors * Returns `NotSynchronized` if the session is not yet ready to accept input. In this case, you either need to start the session or wait for synchronization between clients. #### `poll_remote_clients(&mut self)` Receive UDP packages, distribute them to corresponding UDP endpoints, handle all occurring events and send all outgoing UDP packages. Should be called periodically by your application to give GGRS a chance to do internal work like packet transmissions. #### `current_frame(&self) -> Frame` Returns the current frame of a session. #### `num_players(&self) -> usize` Returns the number of players this session was constructed with. ``` -------------------------------- ### Get Network Statistics Source: https://docs.rs/ggrs/0.12.0/src/ggrs/network/protocol.rs.html Retrieves current network statistics if the protocol is in a synchronized or running state. Returns an error if not synchronized or if insufficient data has been collected. ```rust pub(crate) fn network_stats(&self) -> Result { if self.state != ProtocolState::Synchronizing && self.state != ProtocolState::Running { return Err(GgrsError::NotSynchronized); } let now = millis_since_epoch(); let seconds = (now - self.stats_start_time) / 1000; if seconds == 0 { return Err(GgrsError::NotEnoughData); } let total_bytes_sent = self.bytes_sent + (self.packets_sent * UDP_HEADER_SIZE); let bps = total_bytes_sent / seconds as usize; //let upd_overhead = (self.packets_sent * UDP_HEADER_SIZE) / self.bytes_sent; Ok(NetworkStats { ping: self.round_trip_time, send_queue_len: self.pending_output.len(), kbps_sent: bps / 1024, local_frames_behind: self.local_frame_advantage, remote_frames_behind: self.remote_frame_advantage, }) } ``` -------------------------------- ### SyncLayer::new Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sync_layer.rs.html Creates a new `SyncLayer` instance with the specified number of players and maximum prediction window. ```APIDOC ## new ### Description Creates a new `SyncLayer` instance with given values. ### Parameters - `num_players` (usize) - The total number of players in the game. - `max_prediction` (usize) - The maximum number of frames to predict ahead. ### Returns - `Self` - A new `SyncLayer` instance. ``` -------------------------------- ### Get Milliseconds Since Epoch (Wasm/Native) Source: https://docs.rs/ggrs/0.12.0/src/ggrs/network/protocol.rs.html Provides the current time in milliseconds since the Unix epoch. Handles differences between native and WebAssembly environments. ```rust fn millis_since_epoch() -> u128 { #[cfg(not(target_arch = "wasm32"))] { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("Time went backwards") .as_millis() } #[cfg(target_arch = "wasm32")] { js_sys::Date::new_0().get_time() as u128 } } ``` -------------------------------- ### Create and Synchronize UdpProtocol Endpoint Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/builder.rs.html Creates a new UdpProtocol endpoint with specified parameters and initiates the synchronization process. This is used to set up a new GGRS session. ```rust impl UdpProtocolBuilder { pub fn build( self, handles: Vec, peer_addr: T::Address, local_players: usize, ) -> UdpProtocol { // create the endpoint, set parameters let mut endpoint = UdpProtocol::new( handles, peer_addr, self.num_players, local_players, self.max_prediction, self.disconnect_timeout, self.disconnect_notify_start, self.fps, self.desync_detection, ); // start the synchronization endpoint.synchronize(); endpoint } } ``` -------------------------------- ### Create a new SpectatorSession Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_spectator_session.rs.html Initializes a new SpectatorSession for a spectator. It requires the number of players, a socket for communication, the host's UDP protocol, and parameters for managing frame synchronization. ```rust pub(crate) fn new( num_players: usize, socket: Box>, host: UdpProtocol, max_frames_behind: usize, catchup_speed: usize, ) -> Self { // host connection status let mut host_connect_status = Vec::new(); for _ in 0..num_players { host_connect_status.push(ConnectionStatus::default()); } Self { state: SessionState::Synchronizing, num_players, inputs: vec![ vec![PlayerInput::blank_input(NULL_FRAME); num_players]; SPECTATOR_BUFFER_SIZE ], host_connect_status, socket, host, event_queue: VecDeque::new(), current_frame: NULL_FRAME, last_recv_frame: NULL_FRAME, max_frames_behind, catchup_speed, } } ``` -------------------------------- ### Get Input with Status Source: https://docs.rs/ggrs/0.12.0/src/ggrs/input_queue.rs.html Retrieves player input for a frame, returning a prediction if confirmed input is unavailable. Indicates if the returned input is confirmed or predicted. ```rust pub(crate) fn input(&mut self, requested_frame: Frame) -> (T::Input, InputStatus) { // No one should ever try to grab any input when we have a prediction error. ``` -------------------------------- ### Get Frames Behind Host Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.SpectatorSession.html Returns the number of confirmed frames the spectator session has yet to process. This is useful for understanding synchronization status with the host. ```rust pub fn frames_behind_host(&self) -> usize ``` -------------------------------- ### NetworkStats Default Initialization Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.NetworkStats.html Creates a new NetworkStats instance with default values. ```rust pub fn new() -> Self ``` -------------------------------- ### Handling NetworkStats Availability Source: https://docs.rs/ggrs/0.12.0/ggrs/struct.NetworkStats.html Demonstrates how to safely retrieve network statistics, handling the GgrsError::NotEnoughData error that occurs before sufficient data is available. ```rust match session.network_stats(player_handle) { Ok(stats) => { /* use stats */ } Err(GgrsError::NotEnoughData) => { /* still warming up — ignore */ } Err(e) => return Err(e), } ``` -------------------------------- ### Get Network Statistics for a Player Source: https://docs.rs/ggrs/0.12.0/src/ggrs/sessions/p2p_session.rs.html Retrieves network statistics for a given player handle. Requires the handle to refer to a remote player or spectator and that the connection has been established. ```rust pub fn network_stats(&self, player_handle: PlayerHandle) -> Result { match self.player_reg.handles.get(&player_handle) { Some(PlayerType::Remote(addr)) => self .player_reg .remotes .get(addr) .expect("Endpoint should exist for any registered player") .network_stats(), Some(PlayerType::Spectator(addr)) => self .player_reg .spectators .get(addr) .expect("Endpoint should exist for any registered player") .network_stats(), _ => Err(GgrsError::InvalidRequest { info: "Given player handle not referring to a remote player or spectator" .to_owned(), }), } } ```