Client features – a technical overview

News about the Cafu Engine. Subscribe to the related ImageCafu News feed to stay informed.
User avatar
Site Admin
Joined:2004-08-19, 13:46
Client features – a technical overview

Post by Carsten » 2015-07-16, 10:29

In my previous news post "Client features revisited", I talked about several important client features and how they have recently been improved.

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.

The Basics

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++ const keyword, 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.

If an SC1_FrameInfo message 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 PlayerCommandT struct. 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).
Interim Result

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.

Therefore, in ClientStateInGameT::MainLoop() we make sure to (only) take note of anything that might affect the effects. Most prominently, if a new SC1_FrameInfo server 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!
Best regards,

Who is online

Users browsing this forum: No registered users and 6 guests