Painless peer-to-peer WebRTC networking for rust wasm

Overview

Matchbox

Painless peer-to-peer WebRTC networking for rust wasm applications.

The goal of the Matchbox project is to enable udp-like, unordered, unreliable p2p connections in web browsers to facilitate low-latency multiplayer games.

Introductory blog post

WARNING: This project is in early stages, it will break as things are cleaned up and moved around.

It is currently an all-in-one solution, it comes with:

  • A tiny signalling server, matchbox_server. Written in rust, uses only a couple of megabytes of memory. Also available as a docker image.
  • An example browser game, using bevy, bevy_ggrs and bevy_webgl2, matchbox_demo
  • A socket abstraction for rust wasm, matchbox_socket
    • With a feature, ggrs-socket for providing a ggrs compatible socket.

Live demo

Open each link in a separate browser window (or machine).

When enough players have joined, you should see a couple of boxes, one of which you can move around using the WASD keys.

You can open the browser console to get some rough idea about what's happening (or not happening if that's the unfortunate case).

How it works

WebRTC allows direct connections between peers, but in order to establish those connections, some kind of signalling service is needed. matchbox_server is such a service. Once the connections are established, however, data will flow directly between peers, and no traffic will go through the signalling server.

The signalling service needs to run somewhere all clients can reach it over http or https connections. In production, this usually means the public internet.

When a client wants to join a p2p (mesh) network, it connects to the signalling service and provides a room id. The signalling server then notifies the peers that have already connected about the new peer (sends a new_peer event).

The existing peers then send back WebRTC connection offers through the signalling service to the new client, each of which the new client responds with an "answer". Once the peers have enough information about each other, a WebRTCPeerConnection is established for each peer, and an unreliable, unordered data channel is opened.

All of this, however, is hidden from rust application code. All you will need to do on the client side, is:

  • Create a new socket, and give it a signalling server url and a room id
  • Regularly poll a message loop future that processes new messages. You should do this as often as possible, and at least once per frame. If you have an async runtime, you can simply .await it. If you are using Bevy, it can be spawned as a Bevy io task (see demo).

You will then get notified whenever a new peer data connection has been established, and you will get all packets from peers in a single channel. Packets include a boxed u8 slice and the corresponding client's id.

Similarly, you can send packets to clients using a simple non-blocking method.

Thanks!

  • A huge thanks to Ernest Wong for his Dango Tribute experiment! matchbox_socket is heavily inspired its wasm-bindgen server_socket and Matchbox would probably not exist without it.

License

Except for the demo game all code in this repository dual-licensed under either:

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Demo license

The demo game is derived from https://github.com/mrk-its/bevy_webgl2_app_template, and is fully available under the MIT license. See LICENSE file in that directory. Modifications from the original version are available under the Apache 2 license as well, but some small parts of it are still MIT-only. See: https://github.com/mrk-its/bevy_webgl2_app_template/issues/3

Comments
  • Support native clients

    Support native clients

    This is work in progress to be able to support native clients.

    It splits signalling and messages into separate async futures (and one coordinating one).

    Alternative native implementations for both signalling and messaging have been added.

    • [x] Figure out why no data is getting through
    • [x] Make the demo work on native <-> native
    • [ ] ~Add trickle implementation to wasm (otherwise cross-play will fail)~

    Issue: #7

    opened by johanhelsing 13
  • Add ice trickle support on wasm

    Add ice trickle support on wasm

    Following the same pattern as the native implementation. Now that both wasm and native support trickling, we're one step closer to supporting cross-platform connections.

    TODO:

    • [x] Get trickle wasm <-> wasm working
      • [ ] with negotiated = false?
    • [ ] Get rid of the ugly mutex hack
    • [ ] If webrtc-rs 0.5 is released, remove the candidate sdp_m_line_index hack.
    • [ ] Figure out why connections on the same machine between firefox and native fails during ice
    • [x] Figure out why no data seems to get through on native <-> wasm, even though peer connection state is set to connected
    • [ ] Other todo comments

    Issue: #7

    enhancement 
    opened by johanhelsing 10
  • Add support for native to web cross-play

    Add support for native to web cross-play

    There are some small incompatibilities between the native and wasm implementation that makes connections fail.

    The native version implements ICE candidate trickling, while the wasm version does not. (#16)

    enhancement 
    opened by johanhelsing 7
  • Add ice auth support

    Add ice auth support

    • [x] expose api for setting authentication
    • [x] native implementation
    • [x] wasm implementation
    • [x] should credential and username be String or Option<String>?
    • [x] document new api
    • ~~[ ] implement From trait for new enum~~

    Fixes #3

    enhancement 
    opened by johanhelsing 6
  • add reliable data channel

    add reliable data channel

    As suggested by you in #61 and #25 I tried to implement a pair of data channel - one reliable and one unreliable.

    To be honest I don't have much experience with asynchronous programming or webrtc and I have no idea if what I did makes any sense - I went with the gut and was simply following compiler hints until everything was green again.

    However it seems to work fine in my project with both the reliable and the unreliable data channel. If you tell me that it wasn't total nonsense what I did I will try to make it a bit nicer (maybe I will manage to deduplicate some stuff and rewrite some parts a bit)

    enhancement 
    opened by heartlabs 5
  • Make ICE server URLs configurable

    Make ICE server URLs configurable

    Implements #27

    This is my first public Rust contribution so I am happy about feedback especially about API conventions and borrow rules

    I guess we should also add some tests - but right now I am not sure if this is even possible without major refactoring...

    opened by heartlabs 5
  • cross native and wasm, ice trickle and data channel creation

    cross native and wasm, ice trickle and data channel creation

    This is an attempt to get cross data communication between native and WASM WebRTC, to address https://github.com/johanhelsing/matchbox/issues/47

    The screenshot shows our own app using matchbox, 2 chrome tabs, 1 firefox tab, 2 desktop (native), all communicating with each other. We also tested order of launching and making sure no matter who offers or accepts it still connects. But this is all pretty tricky and might still have some bugs.

    image enhancement 
    opened by rozgo 4
  • Upgrade version of webrtc dependency

    Upgrade version of webrtc dependency

    Recently when building my game with a non-wasm target (MacOS in my case) compile failed with something like

    webrtc-srtp-0.8.9/src/context/srtp.rs:82:43 | 82 | let header = rtp::header::Header::unmarshal(&mut buf)?; | ^^^^^^^^^ function or associated item not found in rtp::header::Header |

    So there seemed to be some kind of version clash between dependencies of matchbox-socket

    An upgrade of 'webrtc' in matchbox-socket's Cargo.toml fixed the issue for me. I had to remove a single line of code as well which addressed a field which didn't exist any more in the new version of the crate - I have no Idea what it was for but at least it compiles again :D

    I don't use matchbox yet in my game for non-wasm target builds so I can't test at the moment if it still works. I hope the PR is still helpful :)

    bug 
    opened by heartlabs 4
  • #13: scoped rooms

    #13: scoped rooms

    Implements #13 This also allows to scope rooms with id. I adapted a test to use scopes, but we might want to create separate tests rather than changing the existing one.

    opened by Vrixyz 4
  • Connectivity issues in the browser

    Connectivity issues in the browser

    I am having a few issues with getting p2p connections between browser tabs. In Firefox, I get the error "WebRTC: ICE failed, add a TURN server and see about:webrtc for more details" around 80% of the time (some browser logs below).

    On Chrome it works every time but takes pretty long (30+ seconds!). In your video on twitter the connection seems to open quickly in firefox, so I am wondering if this changed for you, or if this is an issue with my code/browser or network.

    Interesting parts from firefox webrtc logs in order (but with holes)

    ...
    (id=23622320129 url=http://localhost:4000/)): Skipping STUN server because of address type mis-match
    /build/firefox-fXSvPi/firefox-94.0+build3/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c:175 function nr_socket_multi_tcp_create_stun_server_socket skipping UDP STUN server(addr:IP4:0.0.0.0:3478/UDP)
    /build/firefox-fXSvPi/firefox-94.0+build3/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c:175 function nr_socket_multi_tcp_create_stun_server_socket skipping UDP STUN server(addr:IP6:[::]:3478/UDP)
    /build/firefox-fXSvPi/firefox-94.0+build3/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c:623 function nr_socket_multi_tcp_listen failed with error 3
    (id=23622320129 url=http://localhost:4000/)): failed to create passive TCP host candidate: 3
    
    block above two times
    ...
    (id=23622320129 url=http://localhost:4000/))/CAND-PAIR(kMAM): Pairing candidate IP4:10.16.212.74:46958/UDP
    
    ... 11 seconds later
    Responding with error 400: ICE Failure
    
    ICE(PC:{7ba9e854-55ab-487c-a4bc-76d7fb530c25} <time> (id=27917287425 url=http://localhost:4000/)): Message does not correspond to any registered stun ctx
    
    STUN-CLIENT(kMAM|IP4:10.16.212.74:46958/UDP|IP4:10.16.212.74:44090/UDP(host(IP4:10.16.212.74:46958/UDP)|candidate:0 1 UDP 2122252543 10.16.212.74 44090 typ host)): Received response; processing
    
    STUN-CLIENT(kMAM|IP4:10.16.212.74:46958/UDP|IP4:10.16.212.74:44090/UDP(host(IP4:10.16.212.74:46958/UDP)|candidate:0 1 UDP 2122252543 10.16.212.74 44090 typ host)): nr_stun_process_error_response failed
    
    STUN-CLIENT(kMAM|IP4:10.16.212.74:46958/UDP|IP4:10.16.212.74:44090/UDP(host(IP4:10.16.212.74:46958/UDP)|candidate:0 1 UDP 2122252543 10.16.212.74 44090 typ host)): Error processing response: Retry may be possible, stun error code 400.
    
    ... block above 7 times
    
    STUN-CLIENT(kMAM|IP4:10.16.212.74:46958/UDP|IP4:10.16.212.74:44090/UDP(host(IP4:10.16.212.74:46958/UDP)|candidate:0 1 UDP 2122252543 10.16.212.74 44090 typ host)): Timed out
    
    ICE-PEER(PC:{0a2c1cc7-e126-48d3-b5e1-9daf5d6f401f} <time> (id=23622320129 url=http://localhost:4000/):default)/CAND-PAIR(kMAM): setting pair to state FAILED: kMAM|IP4:10.16.212.74:46958/UDP|IP4:10.16.212.74:44090/UDP(host(IP4:10.16.212.74:46958/UDP)|candidate:0 1 UDP 2122252543 10.16.212.74 44090 typ host)
    
    ICE-PEER(PC:{0a2c1cc7-e126-48d3-b5e1-9daf5d6f401f} <time> (id=23622320129 url=http://localhost:4000/):default)/STREAM(PC:{0a2c1cc7-e126-48d3-b5e1-9daf5d6f401f} <time> (id=23622320129 url=http://localhost:4000/) transport-id=transport_0 - 535cef18:724fdd891babdd9d2d8480c9af275f58)/COMP(1): All pairs are failed, and grace period has elapsed. Marking component as failed.
    
    opened by NiklasEi 4
  • Update matchbox_demo to Bevy 0.8

    Update matchbox_demo to Bevy 0.8

    Technically resolves #32

    Updates Bevy to 0.8 which requires bevy_ggrs 0.10 Removed patch specification for ggrs to update to 0.9.2

    Updated serde_qs to 0.10 and winit to 0.26.1 (removing patch specifier) while I was there

    opened by tracteurblinde 3
  • WIP: get demo working using bevy 0.9

    WIP: get demo working using bevy 0.9

    I know this is blocked on releases of ggrs and bevy_ggrs but I was curious what would be required to get this demo working using bevy 0.9 . Once I got it working I thought I might as well open a PR in case it's useful in the future.

    XRef https://github.com/johanhelsing/matchbox/issues/55

    opened by hfaulds 0
  • Deduplicate and refactor code

    Deduplicate and refactor code

    Adding complex new features like #64 is made more difficult because of mainly two issues in the code structure.

    1. There are two versions of the WebRTC socket and all its components for two compile target (groups) - one for native and one for wasm. All work needs to be done twice and even if both code versions resemble each other in many ways there are also many differences

    2. Most of the code lives in one huge file (per target) which is not too easy to understand - the code structure as it is now doesn't quite fit in my head and I find myself looping over and over through the code trying to understand how one thing affects the other and where the data is flowing. For example even after spending hours with it it's still not clear for me what is the main difference between handshake_offer and handshake_accept or the handling of PeerEvent::NewPeer and PeerEvent::Signal and why they are so similar and if the code can be easily deduplicated or not. Or what exactly is the exact purpose of all the different senders and receivers.

    Ideally before starting some major refactoring work we can get rid of the separate code for wasm. Unfortunately webrtc-rs doesn't yet support wasm and it's not even planned but they opened an issue for that topic with a help wanted label suggesting that they are open to it: https://github.com/webrtc-rs/webrtc/issues/351

    I would be motivated to have a go at it if you also would be looking forward to use this later in matchbox and if you agree that this could dramatically reduce or even eliminate our wasm-specific code.

    Please let me know what you think

    opened by heartlabs 3
  • Support multiple reliable and unreliable data channels

    Support multiple reliable and unreliable data channels

    Exploring your suggestions in #63 regarding #25 I tried an alternative implementation.

    The two main differences beside some fixes are:

    1. It is possible to receive a packet and at the same time understand from which channel it came
    2. I use vectors instead of tuples to prepare for a later extension of the feature for more than two data channels

    I decided to open two PRs for both variants because I am not sure which one is better. I like the simplicity of #63 and I tried to keep it in this PR - it is still possible to simply use the send() and receive() methods to not have to worry about the big world of WebRTC at all and remain blissfully oblivious of the data channels used backstage.

    If the library user would like to have more influence or information over how the packet is or was sent I added two methods:

    • send_enhanced() that return or take a struct respectively where the user can request the packet to be sent via a certain channel (possibly there might be other settings later on but I can't think of any right now to be honest).
    • receive_enhanced() where the user can see via which channel the packet was received

    Of course there would be more convenient APIs if it would be a common usecase to open several data channels where each has its own specific settings and use cases but I love matchbox for its simplicity and this is I think the simplest API for simple use cases.

    Please let me know what you think and if this is going more in a direction that is convenient for you as well.

    enhancement 
    opened by heartlabs 5
  • `ggrs-socket` feature does not work with Bevy 0.9

    `ggrs-socket` feature does not work with Bevy 0.9

    The bevy_ggrs main branch has been fixed to work with Bevy 0.9, but needs currently unreleased changes in ggrs.

    This means the ggrs-socket feature in this repo depends on an older ggrs version than bevy_ggrs.

    After the next version of ggrs is released, we should update the dependency and the matchbox_demo example.

    If you encounter this, a possible workaround is to apply a patch for ggrs:

    Cargo.toml:

    [dependencies]
    bevy = "0.9"
    matchbox_socket = {version = "0.4", features = ["ggrs-socket"]}
    bevy_ggrs = {git = "https://github.com/gschup/bevy_ggrs"}
    
    [patch.crates-io]
    ggrs = {git = "https://github.com/gschup/ggrs"}
    
    enhancement 
    opened by johanhelsing 1
  • Matchbox is broken on Firefox

    Matchbox is broken on Firefox

    I tried following @johanhelsing's blog post series for extreme-bevy and got some success, however after a few seconds my clients randomly disconnect after a short lag spike, while my Chromium peer tells me the following:

    Failed to send: JsValue(InvalidStateError: 
    Failed to execute 'send' on 'RTCDataChannel':
    RTCDataChannel.readyState is not 'open'
    

    coming from here: https://github.com/johanhelsing/matchbox/blob/3f86e86f3df6c4bff6d7b3f1324177d936119635/matchbox_socket/src/webrtc_socket/wasm/message_loop.rs#L107

    After some testing and consulting @gschup on Discord, I found out that this happens every time at least one of the two peers uses Firefox.

    I tested the following configurations, all resulting in the error:

    • Firefox versions:
      • stable 100.0
      • nightly 102.0a1
    • setup:
      • two peers on same machine
      • two peers on two machines
    • operating systems
      • Windows 10
      • macOS Monterey
    • games
      • https://github.com/janhohenheim/extreme-bevy
        • My Cargo.toml has all git dependencies pinned to specific revisions, so this should be very reproducible
        • networking.rs
      • https://helsing.studio/box_game/ (live version)
      • https://gschup.github.io/bevy_ggrs_demo/ (live version)

    untested:

    • Firefox versions:
      • older than 100.0
    • operating systems
      • linux
    • setup
      • more than two peers
    bug 
    opened by janhohenheim 4
Releases(v0.4.0)
  • v0.4.0(Oct 27, 2022)

    What's Changed

    Release announcement

    Most notably, we added support for TURN servers and authentication, added a native implementation (still no cross-play, though, see #47 )

    • Add support for native clients by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/8
    • Simple wasm demo by @sapir in https://github.com/johanhelsing/matchbox/pull/17
    • #13: scoped rooms by @Vrixyz in https://github.com/johanhelsing/matchbox/pull/19
    • update "ggrs_socket" feature and matchbox_demo to ggrs 0.9 by @gschup in https://github.com/johanhelsing/matchbox/pull/20
    • Remove WebRtcNonBlockingSocket by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/23
    • Add KeepAlive request to prevent websockets from auto-closing by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/24
    • Make ICE server URLs configurable by @heartlabs in https://github.com/johanhelsing/matchbox/pull/28
    • Server: Don't panic when signal receiver is unknown by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/33
    • Update matchbox_demo to Bevy 0.8 by @tracteurblinde in https://github.com/johanhelsing/matchbox/pull/39
    • Upgrade version of webrtc dependency by @heartlabs in https://github.com/johanhelsing/matchbox/pull/40
    • Fix deprecated JsValue::from_serde usage by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/42
    • Add ice auth support by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/37
    • Rustdocs for everything! by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/43

    New Contributors

    • @Vrixyz made their first contribution in https://github.com/johanhelsing/matchbox/pull/19
    • @gschup made their first contribution in https://github.com/johanhelsing/matchbox/pull/20
    • @heartlabs made their first contribution in https://github.com/johanhelsing/matchbox/pull/28
    • @tracteurblinde made their first contribution in https://github.com/johanhelsing/matchbox/pull/39

    Full Changelog: https://github.com/johanhelsing/matchbox/compare/v0.3.0...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Oct 27, 2022)

  • v0.2.0(Oct 27, 2022)

    What's Changed

    • Upgrade to clap v3.0 by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/10
    • Use crates.io versions of bevy and bevy_ggrs by @johanhelsing in https://github.com/johanhelsing/matchbox/pull/11

    Full Changelog: https://github.com/johanhelsing/matchbox/compare/v0.1.2...v0.2.0

    Source code(tar.gz)
    Source code(zip)
  • v0.1.2(Oct 27, 2022)

  • v0.1.1(Oct 27, 2022)

  • v0.1.0(Oct 27, 2022)

Owner
Johan Klokkhammer Helsing
Making names and taking games
Johan Klokkhammer Helsing
Peer-to-peer communications library for Rust based on QUIC protocol

qp2p Crate Documentation MaidSafe website SAFE Dev Forum SAFE Network Forum Overview This library provides an API to simplify common tasks when creati

MaidSafe 337 Dec 14, 2022
Simple Peer-to-Peer Exchange

Near Cetificate Devoloper - Demo Simple Peer-to-Peer Exchange On NEAR How it works? See how p2p exchange work here. Exploring The Code The contract co

null 3 Dec 20, 2021
IDP2P is a peer-to-peer identity protocol which enables a controller to create, manage and share its own proofs as well as did documents

IDP2P Experimental, inspired by ipfs, did:peer and keri Background See also (related topics): Decentralized Identifiers (DIDs) Verifiable Credentials

null 5 Oct 31, 2022
Peer-to-peer overlay routing

Rust_Pinecone This is a port of the peer-to-peer overlay routing mechanism Pinecone and aims to be interoperable with it, although it isn't yet becaus

null 3 Aug 2, 2022
Mateversum is a peer-to-peer WebXR metaverse project.

Mateversum ?? Mateversum (pronounced: MAH-tay-ver-sum) is a peer-to-peer WebXR metaverse project. The idea is that you'd be able to connect to a netwo

Ashley 23 Dec 21, 2022
Quick Peer-To-Peer UDP file transfer

qft QFT is a small application for Quick (and really reliable) Peer-To-Peer UDP file transfer. If a friend sent you here... ...look at the "Releases"

Daniel H. 99 Jan 7, 2023
Core library for Lightning Network peer-to-peer nostr platform

Mostro server This document explains how Mostro works. Overview Due to the growing need to be able to operate with Bitcoin without giving up personal

Mostro 16 Jan 4, 2023
A pure Rust implementation of WebRTC API

A pure Rust implementation of WebRTC API

WebRTC.rs 2.7k Jan 7, 2023
A library for easily creating WebRTC data channel connections in Rust

Cyberdeck A library for easily creating WebRTC data channel connections in Rust.

RICHΛRD ΛNΛYΛ 34 Nov 10, 2022
A multiplayer web based roguelike built on Rust and WebRTC

Gorgon A multiplayer web-based roguelike build on Rust and WebRTC. License This project is licensed under either of Apache License, Version 2.0, (LICE

RICHΛRD ΛNΛYΛ 2 Sep 19, 2022
All-batteries included GStreamer WebRTC producer

webrtcsink All-batteries included GStreamer WebRTC producer, that tries its best to do The Right Thing™. Use case The webrtcbin element in GStreamer i

Centricular 75 Dec 14, 2022
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
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
Backroll is a pure Rust implementation of GGPO rollback networking library.

backroll-rs Backroll is a pure Rust implementation of GGPO rollback networking library. Development Status This is still in an untested alpha stage. A

Hourai Teahouse 273 Dec 28, 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
Bevy plugin for the GGRS P2P rollback networking library.

Bevy_GGRS Bevy plugin for the ?? GGRS P2P rollback networking library. The plugin creates a custom stage with a separate schedule, which handles corre

Georg Friedrich Schuppe 120 Jan 6, 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
BitTorrent peer ID registry/parser/(soon) encoder for Rust

BitTorrent peer ID registry/parser/(soon) encoder By convention, BitTorrent clients identify themselves and their versions in peer IDs they send to tr

TORRENTDYNE 3 Oct 16, 2023