Okay, here a âshortâ âtutorialâ / âexplanationâ of what I am basically doing.** Fun fact: The actual client-side-prediction is the easiest part of it all and only takes abound 20 lines.**
You need one central function that calculates all the physics for a frame / step inside of it, which only takes a float DeltaTime as parameter and will be called every tick (or more often when you use substepping, a topic for later on) with the DeltaTime of the tick. So, in this function you apply ALL the forces that will apply to the vehicle this frame / tick.
At the end of this function (but inside of it) another function will be called, that will copy all physics-relevant informations into a special struct that you have to create.
So, what are physics-relevant informations? Basically everything, that you need to 1:1 restore a physical moment in time of the actor: Location, Rotation, Velocity (+Angular), the player input, that can influence the movement of the body (important) and in case of our project, the last SuspensionHingeToGroundDistance, since the damping of the springs will relay on that data. (we work with vehicles)
This struct also needs a float for the deltaTime of the frame and a time. Do not use unreal time here, it has to be world time, and to make it extra difficult, it has to be server synced time.
Okay, quickly: How to sync time with the server?
-What is syncing? Syncing basically means figuring out what time on the client corresponds to what time on the server, WHEN IT ARRIVES THERE. So that you could answer this question on the client: When I send a package to the server at time X, at which time will it be received?
-Why do we need syncing? We need it because when the server will send corrections of the physics-body, it will have a time value. And we need to know to which local time this time correspondates. This makes it easier to understand: You move the forward button on your keyboard, and your body should move forward. Now this information will travel to the server, which takes, letsâs say 100ms. So we have a ping of 100ms. Now the data gets applied on the server and the body starts moving forward there too. Correction data about the physics body will now be sent back to the client. It again takes 100ms. So this corrective data now contains informations about the real position of our body - 200ms ago. It is not where the position of the vehicle NOW is on the server. It is were it was 100ms ago on the server and 200ms ago on the client. So we need synced time, to tell for which moment this correction data was meant to be.
-How do we sync? Itâs fairly easy: Send a request to a server, just a RPC Server-Function call. Note down the local time when you called this function in a variable. When the RPC reaches the server, immediately call a CLIENT function now on the server, which sends the local time of the server back to the client. (The client doesnt need to send any time value to the server). Now back on the client, when the serverTime arrives at the client, we again retreive our current local time, and see how much time has past, since we send the request. For our above example, this should be around 200ms +/- 20ms. Okay, now we have the **RoundTripTime **, which basically is Ping * 2 (Time to travel to server and immeadiately back). Okay, what do we need the serverTime for now? Well, we still need to be able to assign each serverTime a localTime (to tell when any sent information with a timestamp was relevant, remember?). So now we calculate the serverâs system time at the moment we actually sent the request for it. Therefore, we have to substract the ping (RoundTripTime / 2) from the server time, since the serverTime first could be fetched after the 100ms travel of the RPC to the Server. Then we simply can substract the time when we sent the request, and we have the offset between Server and the Client, for example inaccuracys between the internal clocks of the pcâs or differences in timezones. However, this is only the synced time for any exact moment of generall time. Basically, at the time where it was X time on the client, it was Y time on the server. This does not yet fullfill our requirement, to be able to tell âto which moment in local time a package of correction data belongs toâ. Therefore we however just have to add the ping again, to the offset.
So, we have our generall clock offset, that synces time for the aspects of timezones and divergence of the system clocks, and add the ping time to it, to now have a value, with which we can accuratly tell, when a package, send from the server, was meant to be valid on the client.
**
If that all sounds a bit complicatd, here a simple analogy:** On the client, we let Sarah walk forward. She is on Position 6, and now walks to Position 7. We want that the server checks if this calculation is right, or if we need to correct anything. So the information, that Sarah walks forward gets send to the server. This takes 100ms. Then on the server, Sarah also walks forward. The result on the server is also 7. Now this data will be send back to the client. It arrives 100ms seconds later. But oh: On the client, Sarah already walked again and now is on position 8! The corrected values from the server say Sarah is on Position 7⌠Do we have a problem? No! Because with our technique, we now can tell, that this correction value was basically âa responseâ to the moment in time on the client, in which Sarah started walking forward. So the server correction basically says: âAt time XYZ, which corresponds to your time ZYX, Sarah was on Position 7â. The client then checks where Sarah was at time ZYX, sees she was at Position 7, and everything is fine - everything was correct.
So, this took a little bit, right? Where where we. Ah, the struct we need to fill out every physics step with all the physics data. Here we now need the timeOffset we just calculated. We will directly store the synced time (localTime + timeOffset) into the timestep variable, so we now have a way to correlate server and client instances of those structs.
So now that we have our struct filled out, what do we do with it? We need to store it! More precisely, we need a history of physics states. Therefore you can write a new struct, which contains an array of those PhysicsState Structs, and each time a new state gets added, you push all of the existing ones into the next element of the array and write the new state at position 0. So we have our history array, from index 0 (just happened) to index X [maximum] (the state which is the longest time ago of all states). X can be anthing, usually 100 should be fine, but just use more in the beginnign, just to make sure. About the âPush all of them into the next elementâ: Maybe sounds more intuitive when I show you a simple line of code:
for(int32 i = NumberOfItemsInTheHistoryArray - 1; i > 0; i--) HistoryArray* = HistoryArray[i-1];
So, for an array of [10,8,6,4,2], after this cycle we would have [10,10,8,6,4] We abandoned the last element, and now can override the first element with our newest state. 
A simple analogy: A row of parking lots, from left to right. In each lot, a vehicle is parked. When a new vehicle arrives (from the left) and wants to park, the vehicle at the at last lot (right-most) of the row has to to drive away. All the other cars now move one parking lot to the right, so that the left-most spot is free, for the new car.
The hardest parts are done!
Okay, we are still in this function that creates this PhysicsState for us and puts it into the history. On the server, we do not need this history, but we need to send this struct to the client. This will be the current state of the body on the server: The correction data.
For the time value in the struct: You can use the same function to get the âsynced timeâ on the server, however make sure, the timeOffset is set to 0 on the server, because there is no timeOffset, since the server - is the server (eg just write the local time in there).
Replicate this struct to the client, either by writing it into an instance of the struct which is marked âReplicatedâ, or via an RPC Client Function. Use a ReplicatedUsing function in case you choose the Replicated Struct. So, when this data arrives on the client, we are now either in the RPC Client function or the Replicated Using Function, which doesnt make any difference. However, here we now have our server correction. So what we now want to check is, âWas our body at the right position, 200 ms back in timeâ. But actually, we do not really ask this question. It just helps understanding, what we are doing. In fact, we are always treating it that way, as if we would have NEVER had the right position (and rotation, velocity etc).
**What we are now doing is finally the real client-side-prediction. **
1.) We go into our history and search for the entry with the CLOSEST TIME TO THE TIME WITHIN THE CORRECTION-STRUCT RECEIVED FROM THE SERVER. This is now our equivalent local state, where we can check how much divergence we have.
2.) âWrite downâ (note in a variable) on which INDEX this state was in the array.
3.) RESET the vehicle to the values in the correction data. When you have Position, Rotation, Velocity, and Angular Velocity in your PhysicsState struct, set the physics body to those values of the serverState. DO NOT OVERRIDE THE CLIENT INPUT HOWEVER!
5.) Create a copy of the current input of the user.
4.) We now enter a for-loop! We start at the INDEX we marked down, and go down all the way to 0!
As a reference:
for(int32 i = INDEX; i >= 0; i--)
**
In this loop, we now do the following:**
1.) Get the History array element at position i.
2.) Override the clientâs input structure (or values, however you manage them) by the input values from this physicsState that you got in the step above.
3.) Call the PhysicsFunction of your PhysicsBody, and pass the DeltaTime from the physicsState struct as the DeltaTime of the PhysicsFunction(float DeltaTime) parameter. Just make sure to now NOT call the function, that writes new physicStates into the history, because we are not introducing new physicsState but are basically just ârecalculatingâ. Set a boolean or an enum to a value âReplayingâ or âSimulatingâ, to differentiate between those two options (normal call of the function vs call for a âreplayâ)
4.) Tick your physics world by the same amount of time
At the end of the loop, reset your client input back to what it was before (we made a copy of it before).
**Now, what did we do here? **
We figured out the equivalent state in history the data is for, and now are doing this logic âIf I would have been at (X,Y) at time T, where would I be now, when the input would remain the sameâ.
So, we reset our body to the data received by the server, and then calculate all the physical steps we did since this moment in time until the present moment again, with the same input as we had originally.
At the end of the loop, we are back at the current time, and we answered the above logic question âwhere would I be nowâ. You are there now: Your body was just client-side-predicted. 
Edit: Phew, that got longer than I intended it to be 