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)
- CMake 3.21+
- C++17 compatible compiler
- OpenSSL 1.0.0+
- Internet connection (first build fetches dependencies automatically)
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) # macOSWindows (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| 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 ..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.
#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);
|
|
|
|
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.
Full documentation is available at mafianet.mafiahub.dev
Documentation includes:
# 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:8000MafiaNet/
├── 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
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) |
All tests are GoogleTest, built with MAFIANET_BUILD_TESTS=ON and run through CTest. Two suites live under Tests/:
- Unit tests (
Tests/Unit/, labelunit): deterministic and hermetic — no loopback networking, no timing. - Integration tests (
Tests/Integration/, labelintegration): 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 integrationTo debug a single test, run the binary directly with a filter:
./Tests/IntegrationTests --gtest_filter='DispatcherLive.*' --gtest_repeat=100 --gtest_break_on_failure- In-session path-MTU black-hole detection and recovery — the follow-up 0.16.0 deferred. A tunnel dropping large datagrams on the return path only (OpenVPN's defaults do exactly this), or a path shrinking mid-session, passed the one-directional handshake probe and then hung the connection on the first split payload. The reliability layer now recognises the signature — a reliable packet unacked through its resend budget that would actually shrink if the MTU dropped a rung — steps the MTU down the same ladder the handshake probes, and re-splits every queued message that no longer fits
- Packets that already fit the next rung never trigger a step-down, however often they are resent — ordinary packet loss cannot shrink a healthy connection's MTU
- Stuck split messages are rebuilt from the shared data block their fragments reference and re-sent under a fresh split id with ordering indices, exact bit length and ack receipts preserved; ordered channels resume exactly where they blocked
- New API:
ReliabilityLayer::GetCurrentMtuBytes();GetMTUSize()now reports the stepped-down value after a recovery - Non-breaking:
RAKNET_PROTOCOL_VERSIONstays at 7, no message ids move, and the wire format is unchanged — the new bookkeeping is sender-side only
- Fixed: tunnelled clients could not connect — the handshake probes the path in one direction only (the connecting peer pads
ID_OPEN_CONNECTION_REQUEST_1down the MTU ladder, the accepting peer echoes back whatever size arrived), and the result is then frozen for the life of the connection and applied to both directions with no black-hole detection afterwards. Handshake packets are small enough to survive that, so the failure landed on the first split payload: the peer connected and then hung or dropped, depending entirely on its exit node's encapsulation overhead MAXIMUM_MTU_SIZE1492 → 1400, clearing WireGuard (1420) and typical IPSec/IKEv2 (1400) on a 1500-byte path without the peer having to discover anything. The ladder gains 1280 (the IPv6 minimum, and where WireGuard-derived tunnels sit) and 1024, so stepping down costs far less payload capacity than the old 1492 → 1200 jump- Security: an MTU reported by a remote peer is now clamped to
MAXIMUM_MTU_SIZE. It arrives unauthenticated and sizes datagrams built intoMAXIMUM_MTU_SIZE-byte buffers, and the only guard was aRakAssert— compiled out of shipping builds, where a single forged datagram set it to 65535 - Fixed: a locally-refused MTU burned its rung's whole attempt budget (the check compared
Send()'s return value against10040, whichsendtonever returns); asendtoblocking over 100 ms abandoned the connection attempt outright; andConnect()divided by zero for anysendConnectionAttemptCountbelow the number of MTU rungs - Non-breaking:
RAKNET_PROTOCOL_VERSIONstays at 7 and no message ids move. Peers converge on the smaller of their two caps, so a server rebuilt on 0.16.0 caps every connection — including clients still built against 0.15.0 — with no client update required
- Session handshake: connection reporting is now gated on a session-config exchange — an opaque application payload sent in both directions after the transport connection is up but before either side reports a connection.
ID_CONNECTION_REQUEST_ACCEPTED/ID_NEW_INCOMING_CONNECTIONare withheld until the exchange completes, so an application can no longer act on a connection whose configuration has not arrived yet. New API:SetSessionConfig/GetSessionConfig/GetRemoteSessionConfig, plusSetSessionConfigInteractive/AcceptSession/RejectSessionfor servers that inspect a client's payload before admitting it — surfaced through the normalReceive()loop, no network-thread callbacks - Hardened by design: admission control counts handshaking peers, server-to-client handshake messages are bound to the connection's initiator, remote payload buffers are always NUL-terminated beyond the reported length, payloads are capped at 64 KB, and application traffic cannot cross a connection the application has not been told about — in either direction
- Breaking:
RAKNET_PROTOCOL_VERSION6 → 7 — older peers are rejected withID_INCOMPATIBLE_PROTOCOL_VERSIONinstead of stalling. The new message ids come out of the reserved block, soID_USER_PACKET_ENUMdoes not move - Fixed:
GetNumberOfAddresses()always returnedMAXIMUM_NUMBER_OF_INTERNAL_IDS(10) instead of the real local address count (inherited from SLikeNet); theRPC4GlobalRegistrationconstructors never NUL-terminated a maximum-length function name, causing a read past the array
- Bounded relay decode (
SetMaxDecodedSpeakers): relay frames are decoded inOnReceive, insideRakPeer::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 leastRAKVOICE_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 (
0leaves only the existingRAKVOICE_MAX_RELAY_SPEAKERSmemory backstop), and only the relay path is affected — direct peer-to-peer voice does not go throughGetOrCreateChannel. No message ids move, so peers built against 0.13.0 remain wire-compatible
- RakVoice relay mode:
SendFramehas 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/ReceiveFrameFrompull 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 —
OnClosedConnectionnever fires for them because relay speakers are peers of the host, not of us.RelayFramevalidates 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
OnVoiceDataon a 1–2 byte packet (with an unsigned underflow reachingopus_decode), aRakAsserton a remotely supplied sample rate that aborts a debug server, that sample rate consumed without checking the read succeeded,OnReceivedispatching ondata[0]with no length check, andOnOpenChannelReplymissing 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_DATAis inserted afterID_RAKVOICE_DATAand shifts every subsequent message id — peers must be rebuilt together
- Batched datagram I/O (
recvmmsg/sendmmsg): on Linux the reliability layer coalesces a tick's outgoing datagrams into a singlesendmmsgand drains the socket with a singlerecvmmsgper burst, instead of onesendto/recvfromper packet — ~31x fewer system calls on a 2560-message reliable-ordered burst (5525 → 178, median of 3 runs understrace -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 forcingerrno38. OnlyENOSYSlatches;EPERMis excluded because a firewall rejecting one destination reports it too RakNetSocket2::SendBatch: new virtual with a portableSend()-loop default and asendmmsgoverride on Linux, returning a datagram count (not a byte total) and mirroringsendmmsg(2)'s "error only if nothing was sent" contract- Testing: 58 unit cases over the batching helpers (partial-send resume,
errnoclassification, recv-slot carry-over stress) plus live integration tests that actually fill batches; newlinux-nativeCI job covering Debug and Release, with the hermetic unit suite no longer retried
- Range-based receive
Peer::incoming(): drains the receive queue with a range-for; each iteration yields a freshPacketPtrfreed at end of scope, andpkt.id()returns theID_TIMESTAMP-aware identifier - Startup builders
Peer::server()/Peer::client(): fluent chain foldingSocketDescriptor+Startup+ result check +SetMaximumIncomingConnections/Connectinto one call;start()returns a move-onlyResult<Peer>whose error preserves the underlyingStartupResult/ConnectionAttemptResult(tagged byPeerStage) instead of collapsing to a bool. Security stays opt-in (secure()/public_key()) - Serialization archives (
mafianet/Archive.h): oneserialize(Ar&)member template describes a type's wire format for both directions;WriteArchive/ReadArchiveadapt aBitStream, recursing into nestedserialize()types and falling through tooperator<</operator>>(incl. per-type specializations) for everything else - Typed message dispatcher (
mafianet/Dispatcher.h):on<T>(handler)auto-assigns ids fromID_USER_PACKET_ENUMin registration order (documented wire contract;on<T>(id, handler)pins explicit ids),on(id, handler)covers system messages,dispatch()skipsID_TIMESTAMPprefixes and hands handlers a deserializedTplus aSender(guid()/peer_guid()/address()/guid_string());encode()is the symmetric write path. Opt-in — the rawswitchstays 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 acceptsSystemAddressorRakNetGUID; rawSend()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/TestsTestInterfaceharness deleted; hermeticUnitTests+ loopbackIntegrationTestsunderTests/, one process per test via CTest,MAFIANET_BUILD_TESTSbuilds everything test-related (requiresMAFIANET_BUILD_STATIC); CI runs ctest with JUnit artifacts on all platforms
- 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 viaInitializeSecurity()) - Canonical type aliases (
mafianet/aliases.h):PeerInterface(RakPeerInterface),Guid(RakNetGUID),Statistics(RakNetStatistics),UnassignedGuid.usingaliases denoting the same types, so old and new names interoperate; legacy names left untouched - RAII handles
Peer&PacketPtr(mafianet/PeerHandle.h): own aRakPeerInterface/ receivedPacketand clean up on scope exit, removing manualDestroyInstance/DeallocatePacketbookkeeping. 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(...)returnsstd::optional<SystemAddress>(sentinel →nullopt) PointGridSectorizer: uniform point grid with O(1)RemoveEntry/MoveEntryvia per-entry hash + swap-remove (early-out on same-cell moves), upsert add/move semantics, duplicate-freeGetEntries, and edge-cell clamping.GridSectorizerleft untouched- Breaking — scoped enum classes: the global
PacketPriority/PacketReliabilityC enums are removed in favour of scopedMafiaNet::Priority/MafiaNet::Reliability. Enumerator order (and the wire field) is preserved, but call sites must update (HIGH_PRIORITY→MafiaNet::Priority::High,RELIABLE_ORDERED→MafiaNet::Reliability::ReliableOrdered);NUMBER_OF_*sentinels are nowconstexprcounts inMafiaNet - Breaking — removed non-thread-safe
RakNetGUID::ToString(void)(shared static buffer); useMafiaNet::to_string(g).c_str() - Bug fix:
PeerHandleno longer dereferences a moved-fromPeerinreceive()
- Strong-typed
PeerGuid: a newenum class PeerGuid : uint64_tnames a peer'sRakNetGUIDvalue distinctly fromNetworkID(an object id), so the two can no longer be passed interchangeably in auint64_t-typed signature — removing a class of silent "passed the wrong id" bugs in ReplicaManager3 glue andvoid(uint64_t)callbacks. Convert withToPeerGuid()/ToGuid()and compare againstUNASSIGNED_PEER_GUID. As a trivially-copyable 8-byte scoped enum it serializes byte-identically throughBitStream(and thereforeVariableDeltaSerializer) to the rawuint64_tit replaces — fully wire-compatible, no netcode bump. Purely additive, no behavioural change
- Optional disconnect reason:
CloseConnectiongains a final optionalconst BitStream *reasonDataargument whose bytes are appended right after theID_DISCONNECTION_NOTIFICATIONmessage 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 + 1forpacket->length - 1bytes. 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 inspectdata[0]are unaffected - Bug fix:
RakPeer::CloseConnectionno longer coerces an unresolved target index (-1) to0and readsremoteSystemList[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
- Virtual worlds (dimensions): new per-entity / per-observer
VirtualWorldIdscoping on top of ReplicaManager3 — the SA-MPSetPlayerVirtualWorld/ routing-bucket model for instanced interiors (e.g. apartments). Players only see entities sharing their virtual world (orVIRTUAL_WORLD_GLOBAL), switchable at runtime with no reconnect. Derive entities fromVirtualWorldReplica3;Connection_RM3getsGet/SetVirtualWorld;ReplicaManager3getsGetConnectionsInVirtualWorld/GetGuidsInVirtualWorldandSetPlayerVirtualWorld. The filter is authority-only, so a downloaded copy never despawns the entity at its owner. SeeSamples/VirtualWorld
- ReplicaManager3:
GetReplicaAtIndexis nowconst, matching the other read accessors (GetReplicaCount,GetConnectionCount,GetConnectionAtIndex) — const methods iterating replicas no longer need aconst_cast. The returnedReplica3*stays non-const. Source-compatible (no break for existing non-const call sites)
- RPC4 user context:
RegisterFunction,RegisterSlot,RegisterBlockingFunctionand theRPC4GlobalRegistrationhandler constructors now take an opaquevoid *contextpassed 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::CloseConnectionno longer dereferences a nullrakNetSocketduring teardown (a pre-existing crash in release builds); falls back to the primary socket - Testing: added
RPC4ContextTest(slot/nonblocking/blocking context); quarantined the flakyManyClientsOneServerDeallocateBlockingTestin CI pending a teardown-race fix - Breaking: RPC4 handler signatures gained a trailing
void *contextand the registration calls take a context argument; there are no compatibility overloads (passnullptrwhen unused)
- Plugins: Added
DirectoryDeltaTransfer::AddFile(filePath, fileName)to queue a single file for upload (complements the recursiveAddUploadsFromSubdirectory)
- Namespace cleanup: Standardized on the
MafiaNetnamespace; removed the legacySLNetmacro in favor of anMNetshorthand alias, and dropped staleRakNetalias 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 (preventingstd::stringdouble-frees), with added length-prefixedstd::stringspecializations - Breaking: legacy include spellings and the
SLNetnamespace macro are removed; serializing non-trivially-copyable types via the genericBitStream::Write/Readis now a compile error
- Cross-platform: Full macOS and Linux support, merged
Socket2definitions, 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
- 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
- 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.
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.
- Discord: Join our server for support and discussion
- Documentation: mafianet.mafiahub.dev
- Issues: Report bugs or request features
- Contributions: Pull requests are welcome!
MafiaNet is released under the MIT License.