This is a (work-in-progress) port of yojimbo to Rust.
This implementation links directly to the netcode and reliable C libraries.
Please note, this project is not endorsed, vetted, or in any way associated with yojimbo
's authors.
MVP tasks:
- Impl client and server connection
- Impl unreliable channels
- Impl reliable channels
- Impl message serialization
- Add CI/CD
- Add Automated Tests (more are still desireable!)
- Add network simulator
- Expose message IDs (either via set/get on Network Message or receive_message_with_id)
- Note: I added this via
with_id
for now but I'm considering just always returning hte message ID withreceive_message
!
- Note: I added this via
- Review unsafe code (the client must be boxed, to work around some UB, see #1)
- Update dependencies (netcode, reliable, and libsodium)
- Review error handling and use Option/Result (need to resolve some panics still)
- Impl bit packer
- Impl client Matcher service
This is more or less a 1-1 port of yojimbo
to Rust, following the C++ API as close as possible, with some ommissions:
- There is no support for blocks (open an issue if you need it)
- There is no serialization framework included in this library (you're probably going to use serde or write your own serializer)
- There is no bit packer (for now)
- There is no API for specifying any allocators (yet)
- The Matcher is not ported yet, so there is no included way to securely get a private key/connect token to your client out-of-the-box.
In lieu of (reliable/ordered) block support, serializing large messages may work. While not ideal, try sending the binary data in chunks over a reliable channel, and then copy the chunk from each message into your block buffer. This should work OK as a stop gap as long as you aren't sending blocks often, e.g. once at the start of a game.
Additional tasks in the backlog:
- fix unnecessary copying of
NetworkMessage
in reliable channels (removeClone
requirement forNetworkMessage
) - fix mas-bandwidth/yojimbo#170
- use a port of reliable or rewrite it, which will remove most unsafe
- make server/client generic over any networking backend, which removes the remaining unsafe
- implement loopback support with netcode, and efficient, copy-free loopback
If you are looking for more information on how to use netcode
and reliable
, definitely read the architecture section below. After that, check out the client and server examples, both in this library and the original yojimbo
. You can work backwards from there (both are very small libraries). Netcode's client and server examples are also very straightforward.
The user only needs to interface with config types and Client
and Server
, but here's a quick look at how things work, which is important to understand proper usage:
- yojimbo provides a
Client
andServer
which have a nice interface to send and receive messages (reliably or unreliably) on a set of "channels" you define. - the
Client
andServer
usenetcode
as the network backend. netcode
takes care of establishing some semblence of a "connection" using its own protocol over UDP (e.g. handshake and keepalives), and comes with some security protections.- on send, the
Client
andServer
hand your message to a channel on the relevant connection. The channel decides when to send/resend the message. - the
Connection
serializes all the available messages (from all the channels) into a single buffer, and notifies the caller (client or server). - when the
Connection
has a buffer ready, the caller (client or server) then sends the buffer to areliable_endpoint_t
, which computes acks for any previously received packets (and possibly fragments the buffer into multiple packets). - on recieve, this happens in reverse;
Connection
deserializes the buffer and hands each message to the relevant channels, where they sit until you callreceive_message
on the client or server.
There are two types of channels: UnreliableUnordered
and ReliableOrdered
. Unreliable never retransmits packets or holds back messages, making it great for things you need to send fast (like physics snapshots and position updates). ReliableOrdered
buffers messages until all the preceding messages are available (and retransmits messages until they are acked), making it perfect for sending authoritative RPC messages, among anything else that needs to definitely happen and happen in order.
Yojimbo is single threaded, and expects you to be calling advance_time
, send_packets
and receive_packets
continously. You can throttle sending by calling send_packets
less frequently (e.g. only call it every 1/15, 1/30, or 1/60 seconds). receive_packets
should be called about as often to prevent the message queues from overfilling (which will force a disconnect). advance_time
needs to be called at least as often, and no less frequently than ClientServerConfig::timeout
to make sure the connection stays alive.
TODO: talk about fragmentation and reliable channels
How you choose to define channels is totally up to you:
- You might have a channel for each player connected,
- or have a channel for RPC and several unreliable channels with different message and size limits (to manage priority),
- or have a channel for every entity/actor (*note that channel count is fixed at startup),
- or have just two channels, and serialize the relevant entity/actor ID in your messages.
Finally, if you have one or more reliable channels, make sure any recievers are sending something back (it doesn't have to be the same channel), otherwise the reliable messages are never acked (this is generally not a problem unless you have some kind of fixed spectator). For all channel types, make sure you are handling messages so the recieve queues don't overflow.
CI runs on Windows and Linux. Mac support is not tested.
Requirements:
- rustc 1.68.0 or above
- clang (required by
bindgen
, used to generate the Rust bindings toreliable
andnetcode
) - libsodium (bundled for windows, must be avialable to pkg-config on linux)
Please note: The build script is not well tested. The cc
crate should select a suitable compiler based on your system. Please open an issue if you run into anything.
You can compile the examples using:
cargo build --example <example>
# start with:
cargo build --example server
# and in a second terminal:
cargo build --example client
If you want to build netcode
and reliable
separately, please view the build instructions in the respective repo.
Helpful hint: if you are on Windows using MSVC with Rust, you don't need a full Visual Studio install, you can use the VS command line tools' msbuild
command after generating the MSVC project files with premake5
(again, see the repos for details).
This library is currently unlicensed, please open an issue if you would like to use it!
Parts of this library are directly modified from yojimbo
and hence governed under the yojimbo license.