HumbleNet started out as research projects at Humble Bundle in 2015 to support an initiative to port peer-to-peer multiplayer games at first to asm.js and it is now time WebAssembly. In 2016, Mozillaas web games program identified the need to enable UDP( User Datagram Protocol ) networking is supportive of web games, and would like to know whether they could work with Humble Bundle to release the project as open source. Humble Bundle graciously concurred, and Mozilla worked with OutOfOrder.cc to polish and document HumbleNet. Today we are releasing the 1.0 version of this library to the world!
Why another networking library?
When the idea of HumbleNet first emerged we knew we could use WebSockets to enable multiplayer gaming on the web. This approach would require us to either replace the entire protocol with WebSockets( the approach taken by the asm.js port of Quake 3 ), or to tunnel UDP traffic through a WebSocket connection to talk to a UDP-based server at a central location.
In order to work, both approaches require a middleman to manage all network traffic between all clients. WebSockets is good for games that require a reliable ordered communication channel, but real-time games require a lower latency solution. And most real-time games care more about receiving the most recent data than getting ALL of the data in order. WebRTC’s UDP-based data channels fills this need perfectly. HumbleNet offer an easy-to-use API wrapper around WebRTC that enables real-time UDP connections between clients utilizing the WebRTC data channel.
What exactly is HumbleNet?
HumbleNet is a simple C API that wraps WebRTC and WebSockets and conceals away all the platform differences between browser and non-browser platforms. The current version of the library exposes a simple peer-to-peer API that allows for basic peer discovery and the ability to easily send data( via WebRTC) to other peers. In this manner, you are able to build a game that runs on Linux, macOS, and Windows, while utilizing any web browser a and they can all communicate in real-time via WebRTC. A This means no central server( except for peer discovery) is needed to handle network traffic for the game. The peers can talk immediately to each other.
HumbleNet itself use a single WebSocket connection to manage peer discovery. This connect only handles petitions such as” let me authenticate with you”, and” what is the peer ID for a server named “bobs-game-server”, and” connect me to peer #2345 “. A After the peer connection is established, video games communicate immediately over WebRTC.
Here is a simple video of me playing Quake 3 against myself. One game running in Firefox 54( general release ), the other in Firefox Developer Edition.
You can find pre-built redistributables at https :// humblenet.github.io /. These include binaries for Linux, macOS, Windows, a C# wrapper, Unity3D plugin, and emscripten( for targeting asm.js or WebAssembly ).
Starting your peer server
Read the documentation about the peer server on the website. In general, for local development, simply starting the peer server is good enough. By default it will run in non-SSL mode on port 8080.
Using the HumbleNet API
Initializing the library
To initialize HumbleNet simply call humblenet_init() and then later humblnet_p2p_init(). The second call will initiate the connection to the peer server with the specified credentials.
humblenet_init (); // this initializes the P2P portion of the library connect to the dedicated peer server with video games token/ secret( used by the peer server to validate the customer ). // the 4th parameter is for future use to authenticate the user with the peer server humblenet_p2p_init( "ws :// localhost :8 080/ ws", "game token", "game secret", NULL );
Getting your local peer id
Before you can send any data to other peers, you need to know what your own peer ID is. This can be done by sporadically polling the humblenet_p2p_get_my_peer_id () function.// initialization loop( getting a peer) static PeerId myPeer= 0; while( myPeer == 0) // allow the polling to run humblenet_p2p_wait( 50 ); // fetch a peer myPeer= humblenet_p2p_get_my_peer_id ();
To send data, we call humblenet_p2p_sendto. A The 3rd parameter is the send mode kind. Currently HumbleNet enforces 2 modes: SEND_RELIABLE and SEND_RELIABLE_BUFFERED. A A The buffered version will attempt to do local buffering of several small messages and send one larger message to the other peer. They will be broken apart on the other end transparently.void send_message( PeerId peer, MessageType type, const char* text, int size) if( sizing> 255) return; uint8_t buff[ MAX_MESSAGE_SIZE ]; buff[ 0]=( uint8_t) form; buff[ 1]=( uint8_t) size; if( sizing> 0) memcpy( buff+ 2, text, sizing ); humblenet_p2p_sendto( buff, size+ 2, peer, SEND_RELIABLE, CHANNEL );
Initial connections to peers
When initially connecting to a peer for the first time you will have to send an initial message several times while the connection is established. The basic approach here is to send a hello message once a second, and wait for an acknowledge response before presuming the peer is connected. Thus, minimally, any application will need 3 message types: HELLO, ACK, and some kind of DATA message type.if( newPeer.status == PeerStatus :: CONNECTING) time_t now= period( NULL ); if( now> newPeer.lastHello) // try once a second send_message( newPeer.id, MessageType :: HELLO, "", 0 ); startPeerLastHello= now;
To actually retrieve data that has been sent to your peer you need to use humblenet_p2p_peek and humblenet_p2p_recvfrom. If you assume that all packages are smaller than a max sizing, then a simple loop-the-loop like this can be done to process any pending messages. A Note: Messages larger than your buffer sizing is likely to be truncated. Using humblenet_p2p_peek you can see the size of the next message for the specified channel.uint8_t buff[ MAX_MESSAGE_SIZE ]; bool done= false; while (! done) PeerId remotePeer= 0; int ret= humblenet_p2p_recvfrom( buff, sizeof( buff ),& remotePeer, CHANNEL ); if( ret 0) // we received data process it process_message( remotePeer, buff, sizeof( buff ), ret ); else // 0 return value means no more data to read done= true;
Shutting down the library
To disconnect from the peer server, other clients, and shut down the library, simply call humblenet_shutdown.humblenet_shutdown ();
Finding other peers
HumbleNet currently offer a simple "DNS" like technique of locating other peers. A To use this you simply register a name with a client, and then create a virtual peer on the other clients. Take the client-server style approach of Quake3 for example- and have your server register its name as "awesome42."humblenet_p2p_register_alias( "awesome4 2" );
Then, on your other peers, create a virtual peer for awesome4 2.PeerID serverPeer= humblenet_p2p_virtual_peer_for_alias( "awesome4 2" );
Now the client can send data to serverPeer and HumbleNet will take care of translating the virtual peer to the actual peer once it resolves the name.
We have two systems on the roadmap that will improve the peer discovery system. A One is an event system that allows you to request a peer to be resolved, and then apprises you when itas resolved. The second is a proper hall system that allows you to create, search, and join lobbies as a more generic the ways and means of discovering open games without needing to know any name up front.
We have a roadmap of what we plan on adding now that the project is released. Keep an eye on the HumbleNet site for the latest development.
Future work items include :P TAGEND Event API Allows a simple SDL2-style polling event system so that game code can easily check for various events from the peer server in a cleaner way, such as connects, disconnections, etc. Lobby API Utilizes the Event API to build a means of creating foyers on the peer server that are intended to locate game sessions( instead of having to register aliases ). WebSocket API Adds in support to easily connect to any websocket server with a clean simple API.