Skip to content

3. Main Loop

Georg Friedrich Schuppe edited this page Feb 18, 2022 · 7 revisions

In your main game loop, you should call advance_frame() in fixed intervals. How to do that exactly depends heavily on your software stack. You can also check out 👉this article or 👉this article to learn more about running your own gameloop.

For full code examples, take a look at:

Polling Remote Clients

sess.poll_remote_clients();

Polling remote clients will receive and handle incoming UDP packets and send queued packets to other remote clients. In order to establish a connection between sessions, call this function repeatedly until sessions are synchronized. While the game is running and you are calling advance_frame() regularily, GGRS works without calling this method, but frequent polling leads to timely communication between sessions. If you have spare time between rendering and updating your game, poll remote clients.

Main Loop Example Schema

   // time variables for tick rate
    let mut last_update = Instant::now();
    let mut accumulator = Duration::ZERO;

    loop {
        // communicate, receive and send packets
        sess.poll_remote_clients();

        // print GGRS events
        for event in sess.events() {
            println!("Event: {:?}", event);
        }

        // this is to keep ticks between clients synchronized.
        // if a client is ahead, it will run frames slightly slower to allow catching up
        let mut fps_delta = 1. / FPS;
        if sess.frames_ahead() > 0 {
            fps_delta *= 1.1;
        }

        // get delta time from last iteration and accumulate it
        let delta = Instant::now().duration_since(last_update);
        accumulator = accumulator.saturating_add(delta);
        last_update = Instant::now();

        // if enough time is accumulated, we run a frame
        while accumulator.as_secs_f64() > fps_delta {
            // decrease accumulator
            accumulator = accumulator.saturating_sub(Duration::from_secs_f64(fps_delta));

            // frames are only happening if the sessions are synchronized
            if sess.current_state() == SessionState::Running {
                // add input for all local  players
                for handle in sess.local_player_handles() {
                    sess.add_local_input(handle, game.local_input())?;
                }

                match sess.advance_frame() {
                    Ok(requests) => game.handle_requests(requests),
                    Err(GGRSError::PredictionThreshold) => {
                        println!("Frame {} skipped", sess.current_frame())
                    }

                    Err(e) => return Err(Box::new(e)),
                }
            }
        }

        // render the game state
        game.render();
    }
Clone this wiki locally