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 untested alpha stage. At time of writing, the public facing API is largely stable, but a large portion of the functionality provided is still untested in real games.

Differences with the C++ implementation

  • (Almost) 100% pure safe Rust. No unsafe pointer manipulation.
  • Type safety. backroll-rs heavily utilizes generics to allow for
  • Abstracted transport layer protocols - integrate and use any transport layer library you need. Comes with a raw UDP socket based implementation.
  • Configurable at compile time - Almost all Backroll types takes a BackrollConfig generic type parameter that specifies session constants. Does not require forking to change the hardcoded constants.
  • 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. (Complete, untested)
  • backroll_transport - An isolated set of transport layer abstractions.
  • backroll_transport_udp - A transport layer implementation using raw UDP sockets.
  • bevy_backroll - a integration plugin for bevy. (In Progress, untested).
Comments
  • 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
  • Bump actions/cache from 2 to 3

    Bump actions/cache from 2 to 3

    Bumps actions/cache from 2 to 3.

    Release notes

    Sourced from actions/cache's releases.

    v3.0.0

    • This change adds a minimum runner version(node12 -> node16), which can break users using an out-of-date/fork of the runner. This would be most commonly affecting users on GHES 3.3 or before, as those runners do not support node16 actions and they can use actions from github.com via github connect or manually copying the repo to their GHES instance.

    • Few dependencies and cache action usage examples have also been updated.

    v2.1.7

    Support 10GB cache upload using the latest version 1.0.8 of @actions/cache

    v2.1.6

    • Catch unhandled "bad file descriptor" errors that sometimes occurs when the cache server returns non-successful response (actions/cache#596)

    v2.1.5

    • Fix permissions error seen when extracting caches with GNU tar that were previously created using BSD tar (actions/cache#527)

    v2.1.4

    • Make caching more verbose #650
    • Use GNU tar on macOS if available #701

    v2.1.3

    • Upgrades @actions/core to v1.2.6 for CVE-2020-15228. This action was not using the affected methods.
    • Fix error handling in uploadChunk where 400-level errors were not being detected and handled correctly

    v2.1.2

    • Adds input to limit the chunk upload size, useful for self-hosted runners with slower upload speeds
    • No-op when executing on GHES

    v2.1.1

    • Update @actions/cache package to v1.0.2 which allows cache action to use posix format when taring files.

    v2.1.0

    • Replaces the http-client with the Azure Storage SDK for NodeJS when downloading cache content from Azure. This should help improve download performance and reliability as the SDK downloads files in 4 MB chunks, which can be parallelized and retried independently
    • Display download progress and speed
    Changelog

    Sourced from actions/cache's changelog.

    Releases

    3.0.0

    • Updated minimum runner version support from node 12 -> node 16

    3.0.1

    • Added support for caching from GHES 3.5.
    • Fixed download issue for files > 2GB during restore.

    3.0.2

    • Added support for dynamic cache size cap on GHES.

    3.0.3

    • Fixed avoiding empty cache save when no files are available for caching. (issue)

    3.0.4

    • Fixed tar creation error while trying to create tar with path as ~/ home folder on ubuntu-latest. (issue)

    3.0.5

    • Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. (PR)

    3.0.6

    • Fixed #809 - zstd -d: no such file or directory error
    • Fixed #833 - cache doesn't work with github workspace directory

    3.0.7

    • Fixed #810 - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.

    3.0.8

    • Fix zstd not working for windows on gnu tar in issues #888 and #891.
    • Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable SEGMENT_DOWNLOAD_TIMEOUT_MINS. Default is 60 minutes.

    3.0.9

    • Enhanced the warning message for cache unavailablity in case of GHES.

    3.0.10

    • Fix a bug with sorting inputs.
    • Update definition for restore-keys in README.md

    3.0.11

    • Update toolkit version to 3.0.5 to include @actions/core@^1.10.0
    • Update @actions/cache to use updated saveState and setOutput functions from @actions/core@^1.10.0
    Commits
    • 9b0c1fc Merge pull request #956 from actions/pdotl-version-bump
    • 18103f6 Fix licensed status error
    • 3e383cd Update RELEASES
    • 43428ea toolkit versioon update and version bump for cache
    • 1c73980 3.0.11
    • a3f5edc Merge pull request #950 from rentziass/rentziass/update-actions-core
    • 831ee69 Update licenses
    • b9c8bfe Update @​actions/core to 1.10.0
    • 0f20846 Merge pull request #946 from actions/Phantsure-patch-2
    • 862fc14 Update README.md
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies github_actions 
    opened by dependabot[bot] 0
  • Update serial_test requirement from 0.6 to 0.9

    Update serial_test requirement from 0.6 to 0.9

    Updates the requirements on serial_test to permit the latest version.

    Release notes

    Sourced from serial_test's releases.

    v0.9.0

    What's Changed

    New Contributors

    Full Changelog: https://github.com/palfrey/serial_test/compare/v0.8.0...v0.9.0

    Commits

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies rust 
    opened by dependabot[bot] 0
  • Update serial_test requirement from 0.9 to 0.10

    Update serial_test requirement from 0.9 to 0.10

    Updates the requirements on serial_test to permit the latest version.

    Release notes

    Sourced from serial_test's releases.

    v0.10.0

    What's Changed

    New Contributors

    Full Changelog: https://github.com/palfrey/serial_test/compare/v0.9.0...v0.10.0

    Commits
    • d72deb0 0.10.0
    • f965804 Merge pull request #85 from palfrey/1.51-security
    • 44256fd Update regex and remove thread_local to fix security issues
    • 29b6e74 Merge pull request #84 from palfrey/1.51.0
    • f4e3cb5 Downgrade a bunch of stuff for 1.51.0 compat
    • 4330da4 Merge pull request #82 from palfrey/actual-async
    • 177a4f3 Merge branch 'main' into actual-async
    • cb516b9 Merge pull request #83 from palfrey/remove-timeout
    • 34529be Fix use of start only in logging
    • dbd39cf Remove timeout_ms
    • Additional commits viewable in compare view

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies rust 
    opened by dependabot[bot] 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
Owner
Hourai Teahouse
Western doujinsoft circle that makes Touhou fangames
Hourai Teahouse
The Rust Implementation of libp2p networking stack.

Central repository for work on libp2p This repository is the central place for Rust development of the libp2p spec. Warning: While we are trying our b

libp2p 3k Jan 4, 2023
Final Project for "Computer Networking Security": A Layer-3 VPN implementation over TLS

Final Project for "Computer Networking Security": A Layer-3 VPN implementation over TLS

Siger Yang 2 Jun 7, 2022
A simple message based networking library for the bevy framework

Spicy Networking for Bevy bevy_spicy_networking is a solution to the "How do I connect multiple clients to a single server" problem in your bevy games

Cabbit Studios 67 Jan 1, 2023
Cross-platform, low level networking using the Rust programming language.

libpnet Linux ∪ OS X Build Status: Windows Build Status: Discussion and support: #libpnet on freenode / #rust-networking on irc.mozilla.org / #rust on

null 1.8k Jan 6, 2023
A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...

Tokio A runtime for writing reliable, asynchronous, and slim applications with the Rust programming language. It is: Fast: Tokio's zero-cost abstracti

Tokio 18.7k Dec 30, 2022
Painless peer-to-peer WebRTC networking for rust wasm

Matchbox Painless peer-to-peer WebRTC networking for rust wasm applications. The goal of the Matchbox project is to enable udp-like, unordered, unreli

Johan Klokkhammer Helsing 363 Jan 5, 2023
A pure Rust implementation of WebRTC API

A pure Rust implementation of WebRTC API

WebRTC.rs 2.7k Jan 7, 2023
rseip (eip-rs) - EtherNet/IP in pure Rust

rseip rseip (eip-rs) - EtherNet/IP in pure Rust Features Pure Rust Library Asynchronous Extensible Explicit Messaging (Connected / Unconnected) Open S

joylei 18 Dec 27, 2022
Pure rust mqtt cilent

NOTE: Archived. No further development under this repo. Follow progress of a different implementation here Pure rust MQTT client which strives to be s

Ather Energy Pvt Ltd 201 Dec 2, 2022
Eclipse iceoryx2â„¢ - true zero-copy inter-process-communication in pure Rust

iceoryx2 - Zero-Copy Lock-Free IPC Purely Written In Rust Introduction Performance Getting Started Publish Subscribe Events Custom Configuration Suppo

null 136 Jan 1, 2024
The gRPC library for Rust built on C Core library and futures

gRPC-rs gRPC-rs is a Rust wrapper of gRPC Core. gRPC is a high performance, open source universal RPC framework that puts mobile and HTTP/2 first. Sta

TiKV Project 1.6k Jan 7, 2023
A µTP (Micro/uTorrent Transport Library) library implemented in Rust

rust-utp A Micro Transport Protocol library implemented in Rust. API documentation Overview The Micro Transport Protocol is a reliable transport proto

Ricardo Martins 134 Dec 11, 2022
Futures-based QUIC implementation in Rust

Pure-rust QUIC protocol implementation Quinn is a pure-rust, future-based implementation of the QUIC transport protocol undergoing standardization by

null 2.6k Jan 8, 2023
neqo — an Implementation of QUIC written in Rust

Neqo, an Implementation of QUIC written in Rust To run test HTTP/3 programs (neqo-client and neqo-server): cargo build ./target/debug/neqo-server [::]

Mozilla 1.6k Jan 7, 2023
A small holepunching implementation written in Rust (UDP)

rust-udp-holepunch A small holepunching implementation written in Rust (UDP) Prerequisites Your rendezvous server must lay in a network which doesn't

Amit Katz 8 Dec 26, 2022
Rust implementation of PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols

Rust PRECIS Framework libray PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols as descr

Santiago Carot-Nemesio 1 Oct 20, 2022
A high performence Socks5 proxy server with bind/reverse support implementation by Rust.

rsocx A high performence Socks5 proxy server with bind/reverse support implementation by Rust Features Async-std No unsafe code Single executable Linu

b23r0 259 Jan 6, 2023
Interactive bind/reverse PTY shell with Windows&Linux support implementation by Rust.

Cliws Lightweight interactive bind/reverse PTY shell with Windows&Linux support implementation by Rust. Features WebSocket Full pty support: VIM, SSH,

b23r0 215 Dec 3, 2021
Rustus - TUS protocol implementation in Rust.

Rustus Tus protocol implementation written in Rust. Features This implementation has several features to make usage as simple as possible. Rustus is r

Pavel Kirilin 74 Jan 1, 2023