Ramblings of an aging IT geek
← Ramblings of an aging IT geek
gamedev

when reliable rpcs aren't, in unreal

A note on Unreal's RPC reliability model and the easy mistake of leaning on reliable RPCs for high-frequency state, with the failure mode it causes.

A game development scene on screen

Unreal hands you Reliable and Unreliable as a UFUNCTION specifier and it reads like a quality setting. It isn't. Reliable doesn't mean "important", it means "the engine will keep retrying delivery and preserve ordering until it arrives". That guarantee has a cost, and if you reach for it by default you will eventually wedge a connection.

Here's the shape of the thing:

UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon(const FVector& Direction);

UFUNCTION(NetMulticast, Unreliable)
void MulticastSpawnTracer(const FVector& Start, const FVector& End);

The fire input is a one-off, discrete event that genuinely matters, so reliable is correct. The tracer effect is cosmetic and happens constantly, so unreliable is correct. Get those two backwards and you're in trouble.

Networking code on a terminal

The footgun is sending high-frequency state over reliable RPCs. Reliable RPCs share a buffer per connection, and that buffer is not infinite. If you spew reliable calls faster than the connection can acknowledge them, the buffer fills, and Unreal's response to an overflowing reliable buffer is to close the connection. The player doesn't see "minor packet loss", they see a disconnect. I watched a build do exactly this: someone had moved a per-tick position update onto a reliable multicast because an occasional visual hitch was annoying them, and under any real player count the server started kicking people. The logs said the reliable buffer overflowed. They were right.

The general rule that's served me well:

  • Discrete, infrequent, must-happen-once events: reliable. Firing, picking up an item, dying.
  • Continuous state that's re-sent constantly anyway: unreliable, or better, replicated properties.

That last point is the real answer most of the time. If you're tempted to fire an RPC every tick to keep something in sync, you almost certainly want a replicated property instead. Property replication is built for exactly this: the engine sends deltas, handles relevancy, and won't fall over because you sent too many of them. RPCs are for events. Replicated properties are for state. Reliability is a delivery guarantee, not a measure of how much you care.