As promised there, although this is rather a technical documentation than suitable prose for a news post, I would still like to provide a brief overview on how the previously covered features work and how they interact with each other.
As implemented in class
ClientStateInGameT, a client that has joined a game world has these crucial tasks to complete in each video frame:
Code: Select all
// Draw the current state of the game world on screen.
// Everything else that is important in each game loop:
// - process any network packets that might have arrived from the server
// (apply server updates and run reprediction as described below),
// - collect and process player input.
For a start, we can assume that the
Render()method (and any nested
Draw()methods) that are responsible for rendering the client world are "constant". As explained later, this is not exactly true in the strict sense of the C++
constkeyword, but it is true in the sense that when the drawing is done and the
CaClientWorldT::Draw()method returns, the client's world state is exactly the same as it was when the drawing began.
That leaves the client's main loop (
ClientStateInGameT::MainLoop()) to be considered.
SC1_FrameInfomessage arrived from the server, all game entities (that are relevant for the client) are updated to the state of server frame "X". As the server also indicates the growing ID of our client's player command that it has accounted for up to server frame "X", the locally available player commands that have not yet been accounted for since frame "X" are re-applied ("reprediction").
Player input from mouse and keyboard is collected in each video frame and stored in a
PlayerCommandTstruct. It then is:
- sent to the server (for the server's authoritative processing),
- "predicted": applied locally and stored for future reprediction,
- cleared (for the next loop iteration).
At this point, it is important to note that the above described client implements full networking support with prediction and reprediction, but not yet with any of the extra features mentioned in my previous post.
Especially note that the client's game world is always in state "X" as last received from the server, where additionally the local player input is applied to the client's player entity.
Adding Interpolation, Reconciliation and Client Effects
The crucial idea is that our client world is generally always kept in the above described state: latest server state "X" + prediction.
This is a very important invariant of the prediction feature, because otherwise, if interpolation or other effects modified the world state, the prediction would find a different state when locally applying player input than the server would, which in turn introduces stuttering as it breaks the prediction feature.
ClientStateInGameT::MainLoop()we make sure to (only) take note of anything that might affect the effects. Most prominently, if a new
SC1_FrameInfoserver update arrives, new target values for the interpolations are recorded – but the values themselves are updated as if interpolation didn't exist.
The last piece of the puzzle is in
CaClientWorldT::Draw(), which is actually responsible for rendering the world:
- it activates the interpolation, reconciliation and client effects, updating all variables to their proper values,
- renders the world in the prepared state,
- restores the previous values back to "latest server state X + prediction".
Besides the processing of incoming server updates and outgoing player input, the client has a lot more to accomplish before a good game experience can be rendered. The key idea is to keep the client's world state always at the latest received server state "X", with local prediction applied.
Interpolation, reconciliation and client effects are kept and maintained independently. Although the code for these features affects several source files, this encapsulation helps with readability and correctness.
In this regard, we now have a very good and powerful framework. It is used to implement several important examples already, and I hope to utilize these facilities more often and more extensively in the future – I can still think of a lot of effects that I would like to see but that aren't there yet, e.g. head bopping when the player is walking or running, rotating items, swinging and flickering light source, and so on.
As always, your comments, feed-back or help are very much appreciated!