Skip to content
 
 

Repository files navigation

MafiaHub

MafiaNet

A modern, cross-platform networking engine for multiplayer games

Build & Test Discord License C++17 CMake

Quick StartFeaturesDocumentationCommunity


About

MafiaNet is an actively maintained networking library built for game developers who need reliable, high-performance multiplayer networking. Built on the foundation of RakNet and SLikeNet, MafiaNet delivers battle-tested networking capabilities with modern C++ standards and security practices.

Supported Platforms: Windows, Linux, macOS (primary) | iOS, Android (limited)

Quick Start

Requirements

  • CMake 3.21+
  • C++17 compatible compiler
  • OpenSSL 1.0.0+
  • Internet connection (first build fetches dependencies automatically)

Building

Linux / macOS:

git clone https://github.com/MafiaHub/MafiaNet.git
cd MafiaNet
mkdir build && cd build
cmake ..
cmake --build . -j$(nproc)  # Linux
cmake --build . -j$(sysctl -n hw.ncpu)  # macOS

Windows (Visual Studio):

git clone https://github.com/MafiaHub/MafiaNet.git
cd MafiaNet

# Generate Visual Studio 2022 solution
cmake -G "Visual Studio 17 2022" -A x64 -B build

# Build from command line, or open build/MafiaNet.sln in Visual Studio
cmake --build build --config Release

Build Options

Option Default Description
MAFIANET_BUILD_SHARED ON Build shared library (.dll/.so/.dylib)
MAFIANET_BUILD_STATIC ON Build static library (.lib/.a)
MAFIANET_BUILD_SAMPLES OFF Build 80 sample applications and extensions
MAFIANET_BUILD_TESTS OFF Build test suite

To build with samples:

cmake -DMAFIANET_BUILD_SAMPLES=ON ..

Batched Datagram I/O

On Linux, MafiaNet coalesces multiple datagrams into a single recvmmsg/sendmmsg system call instead of one recvfrom/sendto per packet. This is automatic — there is nothing to configure. On platforms without those syscalls (macOS, Windows, the BSDs) the portable per-datagram paths compile instead; behaviour is identical either way, only the number of system calls differs.

Measured on the 2560-message reliable-ordered burst in Tests/Integration/MmsgBatchLiveTests.cpp (Linux, Release, strace -c, median of 3 runs):

syscall per-datagram batched
sendto 2907 35
sendmmsg 0 58
recvfrom 2618 0
recvmmsg 0 85
total 5525 178

~31x fewer system calls. Up to 64 datagrams (MMSG_BATCH_MAX) are coalesced per call. Counts vary by a percent or two between runs — congestion control decides how many datagrams are ready per tick — so treat the ratio as the result, not the exact figures. Reproduce with:

strace -f -c -e trace=sendmmsg,sendto,recvmmsg,recvfrom \
  ./build/Tests/IntegrationTests \
  --gtest_filter='MmsgBatchLive.LargeReliableOrderedBurstArrivesIntactAndInOrder'

The per-datagram column is what every non-Linux platform runs; to reproduce it on Linux, flip the #if defined(__linux__) batching guards in RakNetSocket2.cpp, RakNetSocket2_Berkley.cpp, ReliabilityLayer.cpp and socket2.h to #if 0.

Basic Usage

#include "mafianet/peerinterface.h"
#include "mafianet/MessageIdentifiers.h"

// Create a peer
MafiaNet::RakPeerInterface* peer = MafiaNet::RakPeerInterface::GetInstance();

// Start as server
MafiaNet::SocketDescriptor sd(60000, 0);
peer->Startup(32, &sd, 1);
peer->SetMaximumIncomingConnections(32);

// Or connect as client
peer->Connect("127.0.0.1", 60000, nullptr, 0);

// Process incoming packets
MafiaNet::Packet* packet;
while ((packet = peer->Receive()) != nullptr) {
    switch (packet->data[0]) {
        case ID_NEW_INCOMING_CONNECTION:
            printf("Client connected\n");
            break;
        case ID_CONNECTION_REQUEST_ACCEPTED:
            printf("Connected to server\n");
            break;
    }
    peer->DeallocatePacket(packet);
}

// Cleanup
MafiaNet::RakPeerInterface::DestroyInstance(peer);

Features

Core Networking

  • Reliable UDP with automatic retransmission
  • Packet ordering and sequencing
  • Automatic packet splitting for large messages
  • IPv4 and IPv6 dual-stack support
  • SSL/TLS encryption via OpenSSL
  • Connection statistics and monitoring

Multiplayer Systems

  • Peer-to-peer and client-server architectures
  • NAT punchthrough and traversal
  • Host migration
  • Room and lobby management
  • Object replication (ReplicaManager3)
  • Remote procedure calls (RPC4)

Development Tools

  • Packet logger with multiple outputs
  • Network simulator (latency, packet loss)
  • Bandwidth monitoring and limiting
  • Message filtering
  • Comprehensive debug statistics

Extensions

  • RakVoice - Voice chat with Opus codec and RNNoise
  • Autopatcher - Delta patching for game updates
  • FileListTransfer - Reliable file transfer
  • Database - MySQL, PostgreSQL, SQLite integration
  • UPnP - Automatic port forwarding

Plugins

MafiaNet provides a modular plugin system for extending functionality:

Plugin Description
ReplicaManager3 Automatic object replication and state synchronization
RPC4 Remote procedure calls with parameter serialization
FullyConnectedMesh2 P2P mesh networking with host migration
NatPunchthrough NAT traversal for peer-to-peer connections
FileListTransfer Reliable file transfer with progress callbacks
DirectoryDeltaTransfer Incremental directory synchronization
Autopatcher Delta patching system for game updates
RakVoice Voice chat with Opus codec and noise suppression
ReadyEvent Player ready state synchronization
TeamManager Team assignment and balancing
Router2 Message routing through intermediate peers
MessageFilter Security filtering for incoming messages
PacketLogger Network traffic logging and debugging
TwoWayAuthentication Mutual authentication between peers
CloudComputing Distributed data storage and retrieval
Lobby2 Matchmaking and lobby system

See the Plugin Documentation for detailed usage.

Documentation

Full documentation is available at mafianet.mafiahub.dev

Documentation includes:

Building Documentation Locally

# Install dependencies
brew install doxygen        # macOS
sudo apt install doxygen    # Ubuntu/Debian
pip install -r docs/requirements.txt

# Build and serve
cd docs
./build.sh serve            # http://localhost:8000

Project Structure

MafiaNet/
├── Source/
│   ├── include/mafianet/   # Public API headers (include as "mafianet/...")
│   └── src/                # Implementation files
├── Samples/                # 80 example applications
│   ├── ChatExample/        # Simple chat application
│   ├── Ping/               # Basic UDP communication
│   ├── ReplicaManager3/    # Object replication demo
│   ├── NATCompleteClient/  # NAT traversal client
│   ├── NATCompleteServer/  # NAT traversal server
│   ├── RakVoice/           # Voice chat examples
│   ├── FileListTransfer/   # File transfer demo
│   └── Tests/              # Comprehensive test suite
├── DependentExtensions/    # Optional integrations
│   ├── Autopatcher/        # Delta patching system
│   ├── Lobby2/             # Matchmaking and lobbies
│   ├── RakVoice.cpp/h      # Voice communication
│   ├── MySQLInterface/     # MySQL connectivity
│   ├── PostgreSQLInterface/# PostgreSQL connectivity
│   ├── SQLite3Plugin/      # SQLite integration
│   └── RPC3/               # Legacy RPC system
├── docs/                   # Sphinx documentation source
└── cmake/                  # CMake modules and helpers

Dependencies

MafiaNet automatically fetches required dependencies via CMake FetchContent:

Dependency Version Used For
OpenSSL 1.0.0+ Encryption (required, system-installed)
bzip2 - Compression (Autopatcher)
miniupnpc - UPnP port forwarding
Opus 1.5.2 Voice codec (RakVoice)
RNNoise - Noise suppression (RakVoice)

Running Tests

All tests are GoogleTest, built with MAFIANET_BUILD_TESTS=ON and run through CTest. Two suites live under Tests/:

  • Unit tests (Tests/Unit/, label unit): deterministic and hermetic — no loopback networking, no timing.
  • Integration tests (Tests/Integration/, label integration): real UDP over loopback. Each test runs in its own process, serially, with a timeout.
cmake -DMAFIANET_BUILD_TESTS=ON ..
cmake --build .

# Run everything
ctest --output-on-failure

# Only the fast, hermetic unit suite
ctest -L unit

# Only the loopback integration suite
ctest -L integration

To debug a single test, run the binary directly with a filter:

./Tests/IntegrationTests --gtest_filter='DispatcherLive.*' --gtest_repeat=100 --gtest_break_on_failure

Changelog

Version 0.14.0 (Latest)

  • Bounded relay decode (SetMaxDecodedSpeakers): relay frames are decoded in OnReceive, inside RakPeer::Receive, so by the time an application sees a frame the codec work is already done — a speaker cap applied above this layer bounds mixing, not CPU, and there was no way to bound decode at all. At the cap a newly-heard speaker takes the decoder of whichever current speaker has been silent longest, provided it has been idle for at least RAKVOICE_RELAY_EVICT_IDLE_MS; otherwise its frame is dropped undecoded. Preferring eviction over refusal keeps decoders following whoever is actually talking, and the idle requirement stops a steady stream of talkers past the cap from creating and destroying a decoder every frame
  • The count excludes the self-keyed channel, which in relay mode holds outgoing encoder state rather than a remote talker, so a caller asking for n decoders gets n
  • Non-breaking: additive and off by default (0 leaves only the existing RAKVOICE_MAX_RELAY_SPEAKERS memory backstop), and only the relay path is affected — direct peer-to-peer voice does not go through GetOrCreateChannel. No message ids move, so peers built against 0.13.0 remain wire-compatible

Version 0.13.0

  • RakVoice relay mode: SendFrame has always been peer-to-peer, which a dedicated-server game cannot use — clients connect only to the server. In relay mode clients send frames to a host that forwards them without decoding, so the server stays authoritative over who hears whom without running a codec. Relay frames carry the talker's GUID (the sender is now the relay, not the speaker) plus a format-version byte, so a future layout change is rejected rather than misparsed
  • Origin-keyed channels and per-speaker output: frames are keyed by origin instead of packet->guid, so each speaker gets its own decoder rather than collapsing into one; SetPerSpeakerOutput / ReceiveFrameFrom pull one speaker's PCM instead of the pre-mix, which is what makes 3D positioning possible above this layer. Peer-to-peer behaviour is unchanged when relay mode is off
  • Bounded relay state: concurrent relay speakers are capped, and idle relay channels are reaped — OnClosedConnection never fires for them because relay speakers are peers of the host, not of us. RelayFrame validates origin-vs-sender, frame size, packet id and recipient list centrally rather than trusting every host to remember
  • Five pre-existing remote-input bugs fixed, all reachable by any connected peer with no relay mode involved: an out-of-bounds read in OnVoiceData on a 1–2 byte packet (with an unsigned underflow reaching opus_decode), a RakAssert on a remotely supplied sample rate that aborts a debug server, that sample rate consumed without checking the read succeeded, OnReceive dispatching on data[0] with no length check, and OnOpenChannelReply missing the initialisation guard its sibling has
  • Testing: 23 unit cases over the wire layout, hostile relay input, the speaker cap and the channel-open paths
  • Breaking: ID_RAKVOICE_RELAY_DATA is inserted after ID_RAKVOICE_DATA and shifts every subsequent message id — peers must be rebuilt together

Version 0.12.0

  • Batched datagram I/O (recvmmsg / sendmmsg): on Linux the reliability layer coalesces a tick's outgoing datagrams into a single sendmmsg and drains the socket with a single recvmmsg per burst, instead of one sendto/recvfrom per packet — ~31x fewer system calls on a 2560-message reliable-ordered burst (5525 → 178, median of 3 runs under strace -c). Nothing to configure: it is a platform capability guarded by #if defined(__linux__), always on where the syscalls exist, with the portable per-datagram paths compiled everywhere else. Delivery, ordering and reliability are unchanged
  • Runtime fallback on ENOSYS: if a seccomp profile, sandbox, or old kernel does not implement the syscalls, the process latches once and both paths revert to the portable code — verified under a seccomp profile forcing errno 38. Only ENOSYS latches; EPERM is excluded because a firewall rejecting one destination reports it too
  • RakNetSocket2::SendBatch: new virtual with a portable Send()-loop default and a sendmmsg override on Linux, returning a datagram count (not a byte total) and mirroring sendmmsg(2)'s "error only if nothing was sent" contract
  • Testing: 58 unit cases over the batching helpers (partial-send resume, errno classification, recv-slot carry-over stress) plus live integration tests that actually fill batches; new linux-native CI job covering Debug and Release, with the hermetic unit suite no longer retried

Version 0.11.0

  • Range-based receive Peer::incoming(): drains the receive queue with a range-for; each iteration yields a fresh PacketPtr freed at end of scope, and pkt.id() returns the ID_TIMESTAMP-aware identifier
  • Startup builders Peer::server() / Peer::client(): fluent chain folding SocketDescriptor + Startup + result check + SetMaximumIncomingConnections / Connect into one call; start() returns a move-only Result<Peer> whose error preserves the underlying StartupResult / ConnectionAttemptResult (tagged by PeerStage) instead of collapsing to a bool. Security stays opt-in (secure() / public_key())
  • Serialization archives (mafianet/Archive.h): one serialize(Ar&) member template describes a type's wire format for both directions; WriteArchive / ReadArchive adapt a BitStream, recursing into nested serialize() types and falling through to operator<</operator>> (incl. per-type specializations) for everything else
  • Typed message dispatcher (mafianet/Dispatcher.h): on<T>(handler) auto-assigns ids from ID_USER_PACKET_ENUM in registration order (documented wire contract; on<T>(id, handler) pins explicit ids), on(id, handler) covers system messages, dispatch() skips ID_TIMESTAMP prefixes and hands handlers a deserialized T plus a Sender (guid()/peer_guid()/address()/guid_string()); encode() is the symmetric write path. Opt-in — the raw switch stays fully usable
  • Typed Peer::send / Peer::broadcast: serialize-and-send a registered message in one call via the dispatcher registry, with overridable defaults (Priority::High, Reliability::ReliableOrdered, channel 0); destination accepts SystemAddress or RakNetGUID; raw Send() untouched
  • RakVoice built into the core library: header now mafianet/RakVoice.h; Opus and RNNoise are fetched and linked into the core automatically — no separate extension build
  • Full GoogleTest migration: all 29 legacy tests ported, the Samples/Tests TestInterface harness deleted; hermetic UnitTests + loopback IntegrationTests under Tests/, one process per test via CTest, MAFIANET_BUILD_TESTS builds everything test-related (requires MAFIANET_BUILD_STATIC); CI runs ctest with JUnit artifacts on all platforms

Version 0.10.0

  • Umbrella header mafianet/mafianet.h: aggregates the core public headers (RakPeerInterface, types, message IDs, PacketPriority, BitStream, GetTime, Statistics) so the common path only needs #include "mafianet/mafianet.h". Additive — granular headers remain; encryption headers are intentionally omitted (security stays opt-in via InitializeSecurity())
  • Canonical type aliases (mafianet/aliases.h): PeerInterface (RakPeerInterface), Guid (RakNetGUID), Statistics (RakNetStatistics), UnassignedGuid. using aliases denoting the same types, so old and new names interoperate; legacy names left untouched
  • RAII handles Peer & PacketPtr (mafianet/PeerHandle.h): own a RakPeerInterface / received Packet and clean up on scope exit, removing manual DestroyInstance / DeallocatePacket bookkeeping. ChatExample client rewritten to use them
  • Thread-safe GUID value accessors (mafianet/guid_util.h): MafiaNet::to_string(const RakNetGUID&) owns its buffer; connected_address(...) returns std::optional<SystemAddress> (sentinel → nullopt)
  • PointGridSectorizer: uniform point grid with O(1) RemoveEntry/MoveEntry via per-entry hash + swap-remove (early-out on same-cell moves), upsert add/move semantics, duplicate-free GetEntries, and edge-cell clamping. GridSectorizer left untouched
  • Breaking — scoped enum classes: the global PacketPriority / PacketReliability C enums are removed in favour of scoped MafiaNet::Priority / MafiaNet::Reliability. Enumerator order (and the wire field) is preserved, but call sites must update (HIGH_PRIORITYMafiaNet::Priority::High, RELIABLE_ORDEREDMafiaNet::Reliability::ReliableOrdered); NUMBER_OF_* sentinels are now constexpr counts in MafiaNet
  • Breaking — removed non-thread-safe RakNetGUID::ToString(void) (shared static buffer); use MafiaNet::to_string(g).c_str()
  • Bug fix: PeerHandle no longer dereferences a moved-from Peer in receive()

Version 0.9.0

  • Strong-typed PeerGuid: a new enum class PeerGuid : uint64_t names a peer's RakNetGUID value distinctly from NetworkID (an object id), so the two can no longer be passed interchangeably in a uint64_t-typed signature — removing a class of silent "passed the wrong id" bugs in ReplicaManager3 glue and void(uint64_t) callbacks. Convert with ToPeerGuid() / ToGuid() and compare against UNASSIGNED_PEER_GUID. As a trivially-copyable 8-byte scoped enum it serializes byte-identically through BitStream (and therefore VariableDeltaSerializer) to the raw uint64_t it replaces — fully wire-compatible, no netcode bump. Purely additive, no behavioural change

Version 0.8.0

  • Optional disconnect reason: CloseConnection gains a final optional const BitStream *reasonData argument whose bytes are appended right after the ID_DISCONNECTION_NOTIFICATION message ID, so the remote peer can learn why it was dropped (e.g. a kick/ban enum plus a custom string). The receiver reads it like any other body — packet->data + 1 for packet->length - 1 bytes. Only graceful disconnects carry a reason; locally-synthesized notifications (ID_CONNECTION_LOST, timeout/dead-connection) stay payload-less, so always tolerate a zero-length body. Wire-backward-compatible: peers that only inspect data[0] are unaffected
  • Bug fix: RakPeer::CloseConnection no longer coerces an unresolved target index (-1) to 0 and reads remoteSystemList[0] — which targeted an unrelated peer's slot or crashed on an unallocated list; the close socket is now resolved without assuming a valid slot index

Version 0.7.0

  • Virtual worlds (dimensions): new per-entity / per-observer VirtualWorldId scoping on top of ReplicaManager3 — the SA-MP SetPlayerVirtualWorld / routing-bucket model for instanced interiors (e.g. apartments). Players only see entities sharing their virtual world (or VIRTUAL_WORLD_GLOBAL), switchable at runtime with no reconnect. Derive entities from VirtualWorldReplica3; Connection_RM3 gets Get/SetVirtualWorld; ReplicaManager3 gets GetConnectionsInVirtualWorld/GetGuidsInVirtualWorld and SetPlayerVirtualWorld. The filter is authority-only, so a downloaded copy never despawns the entity at its owner. See Samples/VirtualWorld

Version 0.6.1

  • ReplicaManager3: GetReplicaAtIndex is now const, matching the other read accessors (GetReplicaCount, GetConnectionCount, GetConnectionAtIndex) — const methods iterating replicas no longer need a const_cast. The returned Replica3* stays non-const. Source-compatible (no break for existing non-const call sites)

Version 0.6.0

  • RPC4 user context: RegisterFunction, RegisterSlot, RegisterBlockingFunction and the RPC4GlobalRegistration handler constructors now take an opaque void *context passed back to the handler on every call — no more file-static global pointers to route an RPC to an object instance (each registration carries its own context)
  • Bug fix: RakPeer::CloseConnection no longer dereferences a null rakNetSocket during teardown (a pre-existing crash in release builds); falls back to the primary socket
  • Testing: added RPC4ContextTest (slot/nonblocking/blocking context); quarantined the flaky ManyClientsOneServerDeallocateBlockingTest in CI pending a teardown-race fix
  • Breaking: RPC4 handler signatures gained a trailing void *context and the registration calls take a context argument; there are no compatibility overloads (pass nullptr when unused)

Version 0.5.1

  • Plugins: Added DirectoryDeltaTransfer::AddFile(filePath, fileName) to queue a single file for upload (complements the recursive AddUploadsFromSubdirectory)

Version 0.5.0

  • Namespace cleanup: Standardized on the MafiaNet namespace; removed the legacy SLNet macro in favor of an MNet shorthand alias, and dropped stale RakNet alias references
  • Single header set: Collapsed the legacy redirect-stub header layers into the canonical mafianet/... includes
  • BitStream safety: Catch-all serialization now static_asserts on trivially-copyable types (preventing std::string double-frees), with added length-prefixed std::string specializations
  • Breaking: legacy include spellings and the SLNet namespace macro are removed; serializing non-trivially-copyable types via the generic BitStream::Write/Read is now a compile error

Version 0.4.0

  • Cross-platform: Full macOS and Linux support, merged Socket2 definitions, removed deprecated platform back-ends
  • Fixed IPv6 connectivity and initialization issues
  • Upgraded dependencies: OpenSSL 3.6.0, miniupnpc 2.3.3, Opus 1.6.1; dependencies now fetched on demand via CMake
  • Fixed undefined behaviour in congestion control and a null-socket crash in connection teardown
  • CI now runs the full test suite on Linux, macOS and Windows, with numerous test-stability fixes

Version 0.3.0

  • RakVoice: Migrated from Speex to Opus codec with RNNoise noise suppression
  • Added support for 8000, 16000, 24000, 48000 Hz sample rates
  • Voice activity detection using Opus DTX

Version 0.2.0

  • Rebranded from SLikeNet to MafiaNet
  • Updated to C++17 standard
  • Modernized CMake build system
  • Added Sphinx documentation with Breathe integration
  • Removed pre-generated Visual Studio solution files

See CHANGELOG for full history.

Background

MafiaNet continues the legacy of two foundational networking libraries:

  • RakNet (2001-2014) - Industry-standard game networking library by Jenkins Software, used in countless multiplayer games. Acquired and open-sourced by Oculus VR.
  • SLikeNet (2016-2019) - Community continuation by SLikeSoft that modernized RakNet with bug fixes, security patches, and C++11 support.

With SLikeNet no longer maintained, MafiaNet carries the torch forward—providing an actively developed, modern networking solution for the game development community.

Community

License

MafiaNet is released under the MIT License.


Built with passion by the MafiaHub community

About

MafiaNet is a cross-platform network engine written in C++ and specifially designed for games.

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages