(This post turned into a bit of a rubber-ducking session, apologies for that)
I'm working on a first-person game I've wanted to create for a long time. I'm starting by designing the general architecture, which includes the multiplayer architecture.
I'm very keen on the server being 100% authoritative, such that the client only sends inputs. This is easy. I also want to have client prediction. This is also easy. The problem comes when trying to reconcile what the client predicted against what the server calculated.
Most articles (including the venerable Gabriel Gambetta one) discuss client prediction in combination with rollbacks and resimulation. The issue is, my game is intended to be physics-heavy and I don't want to deal with the cost of resimulating potentially many frames in a single tick, not to mention I'm working with Godot and there's currently no way to manually step the physics engine. I don't care about super precise shooting mechanics or some general desync.
My current rough (untested) plan is to have all entities store a buffer of previous states. The server frequently updates each client with what their current “time” value is based on the server's time + halfRTT. By the time the message arrives, on average, the value the server sent should approximately match the server's wall clock time. The client now has a way to record state against the server's time. Then when the server sends a state update to the client, it includes the current server time along with the state. When the client receives the message, it can check back through its stored entity states to find the nearest on to the given server time. It can then compare that against what the server sent, and if it's close enough, ignore it.
The great thing about doing it this way is that the entity can look at an arbitrarily large range of stored states surrounding the time value received from the server. If any of them match the server state or are close enough, we can ignore the server state. This means we can make the predictions arbitrarily forgiving.
But this is where it gets tough. When a server message arrives on the client, the client is already halfRTT ahead of the server. It looks back through its history of previous states and finds the appropriate one that should be close to the server's state at the point it sent its message to the client. If they match, good. If not, what do we do? Well, naively, you could just set the client state to the server state. The problem is, now you've forced the client entity into the past. When the next server update arrives, the client will once again mismatch and be forced to snap to the server states. At this point, unless the player stops moving for a while, you'll be stuck in an endless loop of forcing predicted state back to server state.
I've been wondering whether I could “nudge” the client state towards the server state. Something like this:
Vector3Double positionDelta = State.Position - recordedEntityState.Position;
float positionNudgeRate = 0.01f;
Vector3Double newPosition = Vector3Double.Lerp(recordedEntityState.Position, serverState.Position, positionNudgeRate);
State.Position = newPosition + positionDelta;
That might work. It assumes the prediction was mostly correct. I believe it has the potential to put clients inside walls (and thus subsequent recorded states could be wildly inaccurate if the physics spits them out the other side), but I think it will correct itself over time. As long it's “eventually correct” it should be ok.
For boolean values (like whether the player is holding a weapon currently) it's not such a big deal. We can snap to the server state without too much risk of glitchy behaviour because the value is unlikely to change again soon.
I know most RTSes and MMOs don't do client prediction, but they're also not usually high-speed games and tend to be have point-and-click interfaces. I find it hard to believe that very large FPS games like Star Citizen would be resimulating in the event of a misprediction (the cost and complexity!!), yet they clearly do client prediction.
Are there any resources I could look at for doing client prediction without resimulating? Or if anyone has any clever suggestions I'd love to discuss. Perhaps I should run the client in the past? I'm not sure, I can't get my head around how that would work. I know I've done a pretty rough job of explaining my plan and maybe what I want simply isn't possible but any thoughts welcome.