A generic, zero-allocation UDP game-networking stack in Zig.
Magnet combines typed channels, reliable delivery, congestion control, optional encryption,
streaming, and game-state replication behind one comptime Config. Its protocol core is
sans-IO, so applications retain control of sockets, clocks, scheduling, and memory placement.
- Deterministic memory. Connection tables, queues, pools, and transfer state are fixed by
the compile-time configuration.
magnet.memoryPlan(config)reports exact storage needs. - Typed channels. Each channel binds a delivery mode to a message type, with compile-time checking at application boundaries and raw-byte paths when needed.
- Sans-IO transport. Feed received datagrams into the protocol and poll outgoing datagrams. The same core works in a game loop, a sharded server, or the deterministic simulator.
- Integrated game replication. Prediction, rollback, interpolation, interest management, authority, lag compensation, and large transfers share one configurable stack.
Magnet is currently a 0.x project. Its API and wire format may change before v1, and it
should be treated as pre-production software. See Status for details.
Requires Zig 0.16:
zig fetch --save=magnet git+https://github.com/zigsel/magnet.gitIn build.zig:
const dep = b.dependency("magnet", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("magnet", dep.module("magnet"));One configuration selects the message schema, delivery behavior, congestion controller, security, and resource limits:
const magnet = @import("magnet");
const PlayerInput = struct { tick: u32, move_x: i8, move_y: i8 };
const GameEvent = union(enum) {
spawned: struct { entity: u32 },
damaged: struct { entity: u32, amount: u16 },
};
const Channels = magnet.proto.channels(.{
.input = .{ .mode = .unreliable_sequenced, .Message = PlayerInput },
.events = .{ .mode = .reliable_ordered, .Message = GameEvent },
.bulk = .{ .mode = .reliable_unordered, .Message = void },
});
const Cfg = magnet.Config{
.channels = Channels,
.protocol_id = 0x4D41474E4554,
.app_version = 1,
.congestion = magnet.proto.delivery.cc.Cubic,
.security = .{ .mode = .aead, .connection_ids = true },
.delivery = .{ .fragmentation = true },
.enable_pmtud = true,
};
const Endpoint = magnet.Endpoint(Cfg);
comptime magnet.validateProduction(Cfg);After connection setup, the application-facing loop stays small:
try client.sendTo(server_addr, .input, input);
while (client.pollTransmit(&datagram, now)) |out| {
// Send datagram[0..out.len] to out.addr through an IPv4 or IPv6 UDP driver.
}
// Feed each received UDP datagram back into the same endpoint.
client.feedFrom(from_addr, received_datagram, now);Acknowledgements, loss recovery, retransmission, pacing, congestion control, encryption, and migration run inside that loop. The rest of the stack composes around the same endpoint:
| Area | Included API and behavior |
|---|---|
| Transport | Six reliability modes, typed messages, AEAD, forward secrecy, recovery, congestion control, STUN, ICE, relay, and multipath |
| Replication | magnet.replication and magnet.host.Bridge for prediction, rollback, interpolation, interest, authority, and lag compensation |
| Streaming | magnet.proto.stream for raw bytes, files, live sources, progress, integrity, resume, and flexible storage |
| Runtime | magnet.runtime for IPv4, IPv6, game-loop, sharded, Linux-optimized, simulated, and observable drivers |
Continue with Getting started or browse the runnable examples.
Each layer is usable independently. Unused declarations are not analyzed or linked:
L5 replication/ snapshots, prediction, rollback, interpolation, interest, lag compensation
L4 wire/ derived serialization, bit packing, quantization, delta coding
L3 proto/channel/ reliability modes, ordering streams, WFQ scheduling, packet packing
L2 proto/delivery/ packet numbers, ACK ranges, RACK, RTT/PTO, congestion, fragmentation
L1 proto/conn/ handshake, tokens, AEAD, replay defense, connection IDs, migration
L0 runtime/ std.Io drivers, poll/reactor/sharded/task loops, network simulation
core/ sequence buffers, pools, rings, bitsets, fixed-point helpers
The compiler-enforced dependency graph keeps the protocol and replication layers independent of runtime IO. See Layering and Sans-IO for the design constraints.
Run zig build docs to generate the API reference in zig-out/docs/.
All six layers described above are implemented and exercised in the repository. Magnet is
still a 0.x library, so no API or wire compatibility guarantee is currently offered.
The repository includes deterministic impairment tests, fuzz and soak cases, invariant checks, and real-socket validation. Independent security review and completed, recorded Internet soaks remain external release evidence. A v1 release also requires a frozen wire specification, an explicit compatibility policy, and an owner-selected software license.
Applications remain responsible for account identity, matchmaking, durable game state, deployment orchestration, and abuse response.
zig build test # unit, integration, and conformance tests
zig build examples # build every example
zig build ci # optimization matrix, examples, and API audit
zig build hardening # impairments, fuzz/soak, and invariant checks
zig build security-review # reproducible audit-preparation package
zig build bench # local ReleaseFast microbenchmarksMagnet is informed by established game-networking systems and Internet transport research:
- reliable.io, netcode.io, and yojimbo by Glenn Fiedler for fixed-capacity networking structures, acknowledgements, tokens, and serialization ideas.
- RakNet for reliability modes, ordering streams, path-MTU probing, and fragment management.
- Valve GameNetworkingSockets for scheduling, pacing, authenticated setup, and key-scheduling ideas.
- quinn and QUIC for loss recovery, RTT/PTO estimation, migration, anti-amplification, and sans-IO architecture.
- laminar for module boundaries and injectable transport seams.
- Bevy lightyear for prediction, rollback, interpolation, interest management, and reconciliation ideas.
The borrowed ideas are theirs; the bugs are ours.