Backroll is a pure Rust implementation of GGPO rollback networking library.

Overview

backroll-rs

crates.io Documentation License Discord

Backroll is a pure Rust implementation of GGPO rollback networking library.

Development Status

This is still in an early beta stage. At time of writing, the public facing API is stable, and has undergone limited testing. There may still be notable bugs that have not been found yet.

Differences with the C++ implementation

  • (Almost) 100% pure safe Rust. No unsafe pointer manipulation.
  • Type safety. backroll-rs heavily utilizes generics and associated types to avoid serialization overhead and potentially unsafe type conversions when saving and loading game state.
  • Abstracted transport layer protocols - integrate and use any transport layer library you need. Comes with a raw UDP socket based implementation.
  • Configurable at runtime - Many of the hard-coded constants in GGPO are exposed as configuration parameters during session initialization.
  • Reduced memory usage - Backroll's use of generics potentially shrinks down the sizes of many data types.
  • Vectorized input compression scheme - Backroll utilizes the same XOR + RLE encoding, but it's written to maximize CPU utilization.
  • Multithreaded I/O - All network communications run within an async task pool. I/O polling is no longer manual, nor blocks your game's execution.

Repository Structure

This repo contains the following crates:

  • backroll - the main Backroll interface, intended to be used as the original GGPO.
  • backroll_transport - An isolated set of transport layer abstractions.
  • backroll_transport_udp - A transport layer implementation using raw UDP sockets.
  • backroll_transport_steam - A transport layer implementation using the Steam provided networking utilities. Enables access to the Steam Datagram Relay service.
  • bevy_backroll - a integration plugin for bevy. (Complete, untested).
  • lib - third-party linking dependencies (i.e. Steam) for easier local development

Building Locally

Due to linking requirements for backroll_transport_steam, local builds that include the crate currently only build when the Steamworks SDK is available.

According to steamworks-rs, at build time the STEAM_SDK_LOCATION env var must be set to a copy of the Steamworks SDK to properly link against.

The Backroll repo has an embedded copy of the Steamworks SDK saved within it. If you are using Rust nightly v1.52+, cargo will automatically set this variable and use the embedded copy of the Steamworks SDK. This is unfortunately unavailable to stable Rust until configurable-env stablizes.

If you are not using Steamworks, you can ignore this entirely. However, blanket workspace builds (i.e. cargo build in the repo root) will include the crate by default. Using cargo dependencies via path should still work so long as Steam support is not needed.

Issues
  • Naming Conventions:

    Naming Conventions: "Backroll" prefix redundant?

    The library currently has a number of typenames prefixed with "Backroll," which seems to add a lot of name bloat. The examples seem to deal with this name bloat with use backroll::*;. Rather than typing something like

    use backroll::*;
    // ...
        let local_handle: BackrollPlayerHandle = session_builder.add_player(BackrollPlayer::Local);
    

    would it be more idiomatic to leave the namespace intact and use

        let local_handle: backroll::PlayerHandle = session_builderr.add_player(backroll::Player::Local);
    

    to avoid conflicts?

    I'm not super familiar with Rust naming conventions, so consider this a suggestion, but it seems like this is the convention among other Rust libraries, with names like Context, Sprite, etc going un-prefixed.

    good first issue breaking change 
    opened by 0----0 4
  • Remove Backroll prefixes

    Remove Backroll prefixes

    This PR renames the following:

    • BackrollConfig to Config
    • BackrollPlayer to Player
    • BackrollPlayerHandle to PlayerHandle
    • BackrollEvent to Event
    • BackrollPeer to Peer
    • BackrollPeerConfig to PeerConfig
    • BackrollSync to Sync
    • backroll::sync::Config to PlayerConfig
    • bevy_backroll::BackrollStageCallbacks to StageCallbacks

    To avoid naming conflicts in protocol/mod.rs, transport::Peer is namespaced as TransportPeer and protocol::Event is namespaced to ProtocolEvent.

    I also took the liberty of addressing default clippy warnings.

    Let me know if any of these changes aren't acceptable πŸ™‚

    breaking change 
    opened by seanchen1991 3
  • Does the bevy plugin need to be single threaded?

    Does the bevy plugin need to be single threaded?

    There's a comment saying "to avoid potentially introducing non-deterministic simulation results", but is there a specific unfixable issue? It seems to me that this should be at least configurable, since it should be possible for all the systems to be completely deterministic.

    I apologize if this is documented and I just missed it, but I feel that this should be configurable or have a more descriptive comment saying why it has to be single threaded.

    opened by BOTBrad 2
  • Remove requirement of Hashing from GameState

    Remove requirement of Hashing from GameState

    Currently in Backroll, gamestates that get saved and loaded HAVE to be able to be hashed. This leads to a lot of issues, since important gamestate stuff like floats and vectors aren't able to be hashed. This should be replaced with something more universal. Note: as far as I know I think this issue is with bevy_backroll but it could extend to the original backroll library as well

    opened by ValorZard 2
  • Consider reverting to the C-style ring-buffer for SavedState

    Consider reverting to the C-style ring-buffer for SavedState

    Commit cf0b896c7e56c7916aceb8595fd4cdb04df44ea8 fixed #12 but replaced the SaveState ring buffer with a frame-keyed hashmap instead. This adds the overhead of clearing out the HashMap of old states each time a frame is confirmed by a remote, and potentially allocates more on the heap each time a new state is saved. This indirection is likely not particularly cache friendly.

    However, given this HashMap should normally never be more than 8-20 entries large at a given time, this may be a moot point in terms of performance.

    Leaving this as an issue to revisit this when time permits.

    opened by james7132 1
  • Desync of game state on PredictionBarrierReached

    Desync of game state on PredictionBarrierReached

    Whenever the prediction barrier gets reached due to an influx of inputs, there will be a bunch of jitters, which leads to the players' positions in this sample project getting desynced. The current theory is that the inputs during that frame don't get read.

    bug 
    opened by ValorZard 1
  • Replace SessionCallbacks with a Command Buffer.

    Replace SessionCallbacks with a Command Buffer.

    This implements the proposal from #6.

    • Moves SavedFrame behind a Arc<Mutex<_>> wrapper called SavedCell for safely passing back to the API consumer in the form of Commands. This should allow a safer targeted control
    • SaveState and LoadState are made to be single-use write-through structs that allow loading and saving game state in a controlled manner.
    • SaveState implements Drop in debug mode only, which will panic if the provided client implementation did not write out to its SavedCell before dropping it.
    • Replaced all &mut impl SessionCallbacks<T> with &mut Vec<Command<T>> instead.

    This has the added benefit of removing another generic parameter from all of the associated functions, so compile times should be faster.

    The public facing API is simpler, is more idiomatic than the callbacks being used from before, and removes the nasty lifetime interactions it had with reference based callbacks from before.

    Currently missing:

    • [x] a newtyped Commands and CommandsIterator types that strictly forbid all operations other than non-consuming iteration and sequential construction to ensure correctness on both the client and Backroll's ends.

    Still a draft. There are some notable parts that require alteration to help prevent users of the API from shooting themselves in the foot.

    enhancement breaking change 
    opened by james7132 1
  • P2PSession is not polling the Player<T> reciever

    P2PSession is not polling the Player reciever

    P2PSession is not polling the Player reciever, which means that the queues of network events are not being forwarded to the top level. This usually ends in a prediction limit hit.

    bug 
    opened by james7132 1
  • Proposal: 'Command Buffer' pattern to replace 'Callbacks' structure

    Proposal: 'Command Buffer' pattern to replace 'Callbacks' structure

    Managing a SessionCallbacks-implementing structure that is expected to be able to directly access the current game state can be somewhat non-ergonomic. However, passing control back and forth between Backroll and client code is not necessarily, strictly speaking, necessary. Backroll needs to tell client code "load this state", "advance with these inputs", "save this state to this buffer", etc, but this does not need to be done during Backroll's own processing.

    Therefore, rather than immediately passing control back to client code, Backroll can instead create a list of 'commands' to the client, which the client can execute afterwards all at once. A rough mock-up of this use of the Command pattern might look like this:

    // backroll definition
    enum Command {
        Save(rollback_buffer_idx: usize),
        Advance(inputs, rollback: bool),
        Load(rollback_buffer_idx: usize)
    };
    
    
    // client code
    let command_buffer: Vec<Command> = session.advance_frame();
    
    for command in command_buffer {
        match command {
            Command::Advance(inputs, rollback) => {
                self.state.advance(inputs, rollback);
            }
            Command::Save(rollback_buffer_idx) { 
                session.save(self.save_data(), rollback_buffer_idx);
            }
            Command::Load(rollback_buffer_idx) {
                self.load_data(session.load(rollback_buffer_idx));
            }
        }
    }
    

    This match command { statement effectively replaces the role of the SessionCallbacks struct in describing the client's performance of Save/Load/etc operations, and it could be easier to understand and implement than a separate struct.

    enhancement help wanted breaking change 
    opened by 0----0 1
  • Improve bevy_backroll API ergonomics

    Improve bevy_backroll API ergonomics

    This PR dramatically improves the ergonomics of bevy_backroll, implementing the following:

    • Removes the need for a user-made config type. bevy_backroll provides one now, generic over the input type.
    • Removes most of the user facing generics when setting up the app and systems. No more turbofish in the user-facing code!
    • Implements an automatic saving/loading system for copying Clone-able components and resources into a Backroll save states. This relies on each entity to have a NetworkId component that maintains the entity identity across.
    opened by james7132 0
  • Initial CI setup

    Initial CI setup

    Sets up an initial set of GitHub Actions should run on every push and pull request, which covers rustfmt, clippy, build, and test on three target platforms: Windows, MacOS, and Linux.

    Missing from this configuration:

    • Automatic releases to GitHub releases and crates.io
    • Cargo deny for checking dependency licenses.
    opened by james7132 0
  • Bevy plugin does not create a checksum for the state anymore

    Bevy plugin does not create a checksum for the state anymore

    As of 014fd15da35a74a3fed620f44d0f5bd68a506f48, bevy_backroll is now saving the provided state without any hashing/checksum. This could lead to silent desyncs as no checksum checks will fail in these cases. We should look for a generic way of handling this without reintroducing the Hash bound on Config::State.

    breaking change 
    opened by james7132 2
  • Example for bevy_backroll

    Example for bevy_backroll

    Thank you for this, looks awesome!

    Could we get a minimalistic bevy app example that uses bevy_backroll? I think that would help getting into the whole networking and backroll topic.

    opened by NiklasEi 2
  • Make framerate / TPS configurable

    Make framerate / TPS configurable

    See:

    https://github.com/HouraiTeahouse/backroll-rs/blob/3c6bdc08a17d2f4e87524595a935d7f28de8e7a4/backroll/src/protocol/mod.rs#L38

    which is used for estimating the remote player's frame for timesyncing purposes:

    https://github.com/HouraiTeahouse/backroll-rs/blob/3c6bdc08a17d2f4e87524595a935d7f28de8e7a4/backroll/src/protocol/mod.rs#L740-L753

    On an unrelated note, it that calculation correct? The comment seems to suggest round_trip_time.as_secs() / 2.

    enhancement good first issue 
    opened by ErnWong 2
  • Tracking Issue: Migrate transport level logic into backroll-transport

    Tracking Issue: Migrate transport level logic into backroll-transport

    There are a notable amount of the application level protocol that belongs in a lower level implementation. The heartbeat, the initial synchronization, the magic number management, sequence number, and disconnect timeout are all to establish a unreliable but ordered connection, something Laminar and other reliable UDP implementations already support. This should exist at the backroll-transport layer. A good amount of this is already established in amethyst/laminar#290, and this change be considered a step towards implementing backroll-transport's connection model in Laminar.

    • [ ] Create a wrapper around Peer for ordered unreliable sends. OrderedPeer?
    • [ ] Add a way to notify the main game of incoming connections.
    • [ ] Alter Peer to notify either end of a network interruption.
    • [ ] Move "Synchronizing, Running, Interrupted, Disconnected" state machine to the transport layer.
    • [ ] Move heartbeat task into OrderedPeer
    breaking change 
    opened by james7132 0
  • WebRTC Transport Layer

    WebRTC Transport Layer

    Adding this here just for posterity, since I already know that this is in the plans. This would be pretty easy to test though, since bevy and other rust game frameworks already support the web, with a few being web-only in fact. Having a game with rollback on the web besides Tough Love Arena would be really cool.

    enhancement help wanted 
    opened by ValorZard 3
Owner
Hourai Teahouse
Western doujinsoft circle that makes Touhou fangames
Hourai Teahouse
Good Game, Peace Out Rollback Network SDK

(ζ—₯本θͺžγƒ‰γ‚­γƒ₯γƒ‘γƒ³γƒˆγ―γ“γ‘γ‚‰) What's GGPO? Traditional techniques account for network transmission time by adding delay to a players input, resulting in a sluggish,

Tony Cannon 2.6k Aug 8, 2022
Mongoose Embedded Web Server Library - a multi-protocol embedded networking library with TCP/UDP, HTTP, WebSocket, MQTT built-in protocols, async DNS resolver, and non-blocking API.

Mongoose - Embedded Web Server / Embedded Networking Library Mongoose is a networking library for C/C++. It implements event-driven non-blocking APIs

Cesanta Software 8.6k Aug 8, 2022
The C++ Network Library Project -- cross-platform, standards compliant networking library.

C++ Network Library Modern C++ network programming libraries. Join us on Slack: http://slack.cpp-netlib.org/ Subscribe to the mailing list: https://gr

C++ Network Library 1.9k Aug 9, 2022
Rust bindings for Pixar's Universal Scene Description (USD)

Getting started Make sure you have cppmm built/working. https://github.com/vfx-rs/cppmm Build usd 21.11 Then run bash bind.sh. You need to make sure a

null 26 Jul 30, 2022
ENet reliable UDP networking library

Please visit the ENet homepage at http://enet.bespin.org for installation and usage instructions. If you obtained this package from github, the quick

Lee Salzman 2.2k Aug 9, 2022
canonical libwebsockets.org networking library

Libwebsockets Libwebsockets is a simple-to-use, MIT-license, pure C library providing client and server for http/1, http/2, websockets, MQTT and other

lws-team 3.6k Aug 6, 2022
requests-like networking library using boost for C++

cq == C++ Requests cq == C++ Requests is a "Python Requests"-like C++ header-only library for sending HTTP requests. The library is inspired a lot by

null 11 Dec 15, 2021
C++ networking library including UniConf and a convenient D-Bus API

This is wvstreams, a nominally platform-independent networking and utilities library for C++. Some documentation is in the Docs/ directory. If that

null 27 Dec 29, 2021
Socket and Networking Library using msgpack.org[C++11]

netLink C++ 11 KISS principle networking library. Features: C++ 11 IPv4, IPv6 Protocols: TCP, UDP Enable/Disable blocking mode Join/Leave UDP-Multicas

Alexander Meißner 207 Aug 6, 2022
Simple and small reliable UDP networking library for games

libquicknet Simple and small reliable UDP networking library for games ❗ libquicknet is under development and not suitable for production code ❗ The m

null 23 May 12, 2022
Small and fast cross-platform networking library, with support for messaging, IPv6, HTTP, SSL and WebSocket.

frnetlib Frnetlib, is a cross-platform, small and fast networking library written in C++. There are no library dependencies (unless you want to use SS

Fred Nicolson 22 May 16, 2022
A simple networking library for C++. Designed with games in mind, but can be used for other things

A simple networking library for C++. Designed with games in mind, but can be used for other things. Tested to work on Android and Windows.

Matthew 5 Nov 14, 2019
Single file public domain networking library

zed_net zed_net is a single file, public domain library that provides a simple wrapper around BSD sockets (Winsock 2.2 on Windows), intended primary f

Ian T. Jacobsen 50 Jul 18, 2022
An HTML5 parsing library in pure C99

Gumbo - A pure-C HTML5 parser. Gumbo is an implementation of the HTML5 parsing algorithm implemented as a pure C99 library with no outside dependencie

Google 5k Aug 6, 2022
Asynchronous networking for C

Overview Dyad.c is an asynchronous networking library which aims to be lightweight, portable and easy to use. It can be used both to create small stan

null 1.3k Aug 5, 2022
RakNet is a cross platform, open source, C++ networking engine for game programmers.

RakNet 4.081 Copyright (c) 2014, Oculus VR, Inc. Package notes The Help directory contains index.html, which is full help documentation in HTML format

Facebook Archive 3.1k Aug 14, 2022
WAFer is a C language-based software platform for scalable server-side and networking applications. Think node.js for C programmers.

WAFer WAFer is a C language-based ultra-light scalable server-side web applications framework. Think node.js for C programmers. Because it's written i

Riolet Corporation 694 Aug 11, 2022
Reliable & unreliable messages over UDP. Robust message fragmentation & reassembly. P2P networking / NAT traversal. Encryption.

GameNetworkingSockets GameNetworkingSockets is a basic transport layer for games. The features are: Connection-oriented API (like TCP) ... but message

Valve Software 6.2k Aug 15, 2022
RakNet is a cross platform, open source, C++ networking engine for game programmers.

RakNet 4.081 Copyright (c) 2014, Oculus VR, Inc. Package notes The Help directory contains index.html, which is full help documentation in HTML format

Facebook Archive 3.1k Aug 11, 2022