State-Synchronized Multiplayer Networking
Mark a field and it replicates. Underneath that one line sits a tick-aligned, bit-level engine that your game grows into rather than out of.
Position and rotation ride in 1.5 to 3 bytes per update. Values pack down to the bit, only what changed goes out, packets batch themselves, and any value can be told to send less often than the rest.
Input applies the moment it is pressed, and the server still decides what happened. When the two disagree the client rewinds to the server tick, replays everything since, and carries on without the player noticing.
Interest rules decide who hears about what, which is the lever that sets your ceiling. Memory and bandwidth stay flat as players arrive. At party size the same engine hands one object to several clients and keeps the session alive when the host walks away.
netstandard2.1 with no engine dependency and no per-player fee. Mark a field and it replicates, swap the transport in one line, run it on whatever you already rent, and configure it from launch flags the way deploy pipelines expect.
Create a CoreManager and pick a transport. Every sub-manager wires itself up, the tick loop starts, and what you write from there is game code.
// One engine object, and the same transport type, on both sides. CoreManager core = new(); Synapse transport = (Synapse)await core.TransportManager.AddTransportAsync<Synapse>(); // As the server: listen, and hear about players as they arrive. await transport.ConnectAsync(Invoker.Server); core.ServerManager.ClientAuthenticated += connection => Console.WriteLine($"Player {connection.Id} joined"); // As a client: point at the host and go. Nothing else changes. transport.RemoteHost = IPAddress.Parse("203.0.113.42"); await transport.ConnectAsync(Invoker.Client);