A client-to-server RPC in my little multiplayer prototype was firing most of the time and silently doing nothing the rest of the time. No error, no log, just an action that occasionally didn't happen. On a fast local connection it looked perfect. The moment I added simulated latency it started dropping, and I spent an evening convinced the bug was in my game logic.
It wasn't. The RPC was declared Unreliable.
UFUNCTION(Server, Unreliable, WithValidation)
void ServerFireWeapon();
Unreliable RPCs in Unreal are sent over UDP with no delivery guarantee. That is correct and deliberate for things you send constantly, movement updates, where the next one along makes up for a lost one. It is exactly wrong for a discrete event that must happen, like firing a weapon. Drop that packet and the shot simply never occurs. With no packet loss in testing you never see it.
The fix is one keyword:
UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon();
Reliable guarantees ordered delivery, at the cost of bandwidth and the ability to flood the channel if you spam it. The rule I should have known: reliable for events that must land, unreliable for streams where the latest value is all that matters. I now read the RPC specifier before I read my own logic, because the network layer lies to you quietly when you test on localhost.