#@thekrocker Predicition Thread stuff.

1 messages ยท Page 1 of 1 (latest)

topaz solstice
#

.

quaint badge
#

I am really curious about what would be the headaches. Until now, I escaped terms like client prediction and recoincillation, I thought they would be only used PvP games like instant reaction, and right info.

topaz solstice
#

Let'a ssume we have a simple setup like this. Doesn't matter where this is in, but just so it's not confusing we do this in a Pawn/Character that is possessed and the "DoStuff" Event is called when pressing your Spacebar.

#

So far this doesn't do anything, but the goal is that SomeValue gets incremented by 2 every time you press the Spacebar.

#

So far so good?

quaint badge
#

Alright! Good. We call DoStuffInternal with space bar.

#

which increases our value by +2, like you said

topaz solstice
#

Right now we have a ServerRPC version of DoStuff and some Internal version that actually does the +2.

#

Ah, gimme a sec then.

quaint badge
#

Ah, sorry. You said DoStuff, not DoStuff_Internal.

#

I thought we would be calling the not empty method.

topaz solstice
#

Like, this, right?

quaint badge
#

Yes, I am really curious where this is going. A new illuminating things are coming for me. Thanks for your effort.

topaz solstice
#

Now, let's start with the obvious:

SomeValue isn't even replicated yet. But let's leave it at that for a second.

#

Now, first things first, we just gonna do the Internal stuff locally.

#

Now, obviously, this will cause SomeValue to increase only on the player that presses it.
let's ignore ListenServer for a second, cause that isn't really important for prediction.
So only clients press the key.

#

Now, the next step is to get the update to the Server, so we call the ServerRPC, and let that one also call the Internal one:

#

So far so good, now the value increments on Server and Client, and if we don't need it on any other Client, then this would also be "enough".

#

Now, while the RPC is marked as reliable, it's probably still very naive to think that it would always stay in sync like that.

quaint badge
topaz solstice
#

We could for example have this:

topaz solstice
quaint badge
#

Alright, now we have added condition for internal method

topaz solstice
#

ListenServer is really uninteresting cause it just skips the whole problem anyway, as it is the Server.

topaz solstice
#

This is kept vague cause it can be a lot of things, even things you really can't predict.

#

A simple example: The player could try to uncrouch, which checks if nothing is above them. But since other players are also predicting movement, they could be on top of the player on the server but not yet on the client, so the uncrouch fails on the server.

#

But you could make a very easy case of it failing randomly by doing this:

#

That is of course totally stupid, but I hope you get the idea.

quaint badge
#

Hmmm. Understood, this is an issue actually. If its not synced at right time, It might be really headache.

#

But if we send RPC, it will be executed and server and it will check those but with a delay problem if has ping problem etc.

topaz solstice
#

Right, so now we go one step furthe, cause this is a real issue that needs to be solved (and the CharacterMovementComponent as well as GAS do solve that already in their own way).

quaint badge
#

So you are saying, Prediction is not always great because it still depends on others syncing time

topaz solstice
#

Since the Server and the Client can disagree on the value being incremented, we need one of the two to be the AUTHORITY over this.

#

In scenarios where you really don't care that the player can cheat, you'd handle this largely different.

quaint badge
topaz solstice
#

This is Client Authoritive. The Client does the change and tells the Server.

topaz solstice
#

That is not prediction.

#

But it would solve the problem.

#

The Server simply accepts the value of the Client, even if the Client incremented it by 3.

quaint badge
#

Yeah, in here Server is not validating here right.

#

Would we obligated to send the value with RPC? Or we can also increase the value in the Server? Is that different? I need to see if this is different.

First Case

  1. We change value on local.
    2- Calling RPC server. Server also changes the value.

Second Case

  1. We change value on local.
  2. Calling RPC server, and sending our value directly to the server.
  3. Server Instance of the pawn sets the value directly from sent data.
topaz solstice
#

Gimme a minute. Need to help my wife with something.

quaint badge
#

We now send the value directly to the Server, not tell him to increase value in himself too.

quaint badge
topaz solstice
#

So now let's think about the Server Authoritive way, cause that's the more complex one.

quaint badge
topaz solstice
#

So here not much has changed. I marked the SomeValue variable as replicated.
That means, once the Server increments the variable, it will replicate to the Client.
Before we continue with that, let's see what can all happen.

  1. Server and Client increment the variable, the value will replicate back to the Client, which is the same value, so we are fine.
  2. Server increments, Client doesn't, the value will replicate back to he Client and gets "corrected".
  3. Client increments, Server doesn't, the value will not replicate back to the Client until the next change of it, so both are now de-synced.
topaz solstice
topaz solstice
#

Case 3. is a problem if you want that to be correct on the Client.

#

There are two ways this is addressed, at least two that I know about.

  1. The Client tells the Server, alongside the RPC, what its own result was, and the Server can then send a ClientRPC back to correct the Client.
  2. The Server always replicates the data back, even if not changed, and the Client locally checks if it mispredicted.

The CMC does 1. by sending the final Location the Client ended up on along.
The NetworkPredictionPlugin does 2. by always replicating the whole State of the Simulation. This is a more complex, as you can't mimic this with just a replicated variable.

#

It's most likely sane to stick with 1. for now. I can't even show-case 2. here anyway.

quaint badge
#

Aha. So first one is simply validation? Like in C++ _Validation methods with RPC

topaz solstice
#

Client increments (or not), tells the Server about it, Server increments (or not) and then compares if Server and Client did the same thing. If not, then Server tells Client its own value.

#

That's what the CMC does, more or less. Client sends the Server all its Instructions that are needed to perform the Movement (e.g. keys pressed etc.), as well as the result of it (Location).
Server performs the Movement based on the Instructions and then compares its own result with the Client's.
If this results in a too large difference (there is some threshold, cause it's not always that exact), it sends the whole Movement State (that also includes Velocity etc.) to the Client.

#

Now, if you would do this, and you'd take a bit of time between each Spacebar press, this would probably work.

#

But, if you press the button very quickly, you'll notice a nasty issue.

#

That issue will even show up without a need for a correction.

#

Ah btw, if you handle correction like this, you don't need to make the variable as replicated.

#

I should probably have unticked that for the image.

#

In fact, the CMC doesn't replicate any of its properties. The most it replicates are extra properties in the Character class for SimulatedProxies. Like "are we crouching".

quaint badge
topaz solstice
#

Yeah

quaint badge
#

What is CMC called in long way?

topaz solstice
#

CharacterMovementComponent

#

But you get the problem I will explain now with both, RPC and replicated Variable.

#

And that's the really big headache.

quaint badge
#

Hmm. I can see the reason why i am not really aware of.

quaint badge
#

Thanks for opening my eye very early.

topaz solstice
#

Since the Client predicts the value change, there is a the factor of "time" between the original Spacebar press, and when either the Correction RPC comes back or the replicated Variable comes back.

#
Client: 2 | 4 | 6 | 8 | 10 | 12
Server:         2 | 4 |  6 |  8
#

I offset them to indicate the difference in real-time when the Server and the Client set their values.

#

Now imagine a correction:

Client: 2 | 4 | 6 | 8 | 10 | 12
Server:         2 | 2 |  4 |  6
quaint badge
#

Yeah, Spamming would be a total disaster here.

topaz solstice
#

Server didn't increment the value to 4 in the second case.
Now to have simple values, let's assume each change is 1 second apart.
So the Client took 2 seconds to get from 2 to 4.

#

And the Server increment from 0 to 2 2 seconds after the Client did.
Now let's say this is 100% the same always (no ping changes), so the Client_Correction RPC also takes two seconds to replicate back:

#
        1   2   3   4    5    6   7    8
Client: 2 | 4 | 6 | 8 | 10 | !2 | 4 |  6
Server:         2 | 2 |  4 |  6 | 8 | 10
#

I can't count apparently.

#

Let's numerate these for a sec

#

So what happens here is that the Client prediction is totally stomped in "frame" 6.

#

But the big question is, is that even all of it?

quaint badge
#

๐Ÿ˜‚

topaz solstice
#

:D no

quaint badge
#

Time to add cooldown for every thing can be spammable. ๐Ÿ˜‚

topaz solstice
#

So given it's all delayed, the frames 3, 4, 5, etc. will also come to the Server with wrong values.

quaint badge
#

And ruined your all week

topaz solstice
#
        1   2   3   4    5    6    7    8    9
Client: 2 | 4 | 6 | 8 | 10 | !2 | !4 | !6 | !8 |

                1     2      3      4      5
Server:         2 | 2!=4 | 4!=6 | 6!=8 | 8!=10
#

You'll get a correction for all the other frames that are on the way to the Server, that are all wrongly predicted due to frame 2.

quaint badge
#

N....n....noooooooooooooooooooooooooooooo

topaz solstice
#

For this number example it doesn't seem to bad, but cause it lines up with the values the client would continue to predict after the first correction.

#

But if it would be a more complex example, then frames 7, 8 and 9 of the Client could actually stomp even more things the Client tried to predict.

#

And now comes the even better part. The Client is creating more and more corrections here, because it is sending 4 for frame 7, etc.
But if you follow the Server, it will get to 10 on frame 6, 12 on frame 7, and now the Client's 4 is != the Server's 12.

#

Why is that? Because the Client never re-applied its predicted frames after receiving the correction.

#

That's what is called "Reconciliation".

#

The correction part is called "Rollback".

quaint badge
topaz solstice
#

So what the Client would actually have needed to do, is to keep track of what it did locally, apply the Rollback, and the redo all "newer" things it did, all in one frame.

#

That would cause this:

#
              1    2    3    4    5     6    7    8    9
Client:       2 |  4 |  6 |  8 | 10 | !10 | 12 | 14 | 16 |
Saved"Move": +2   +2   +2   +2   +2    +2   +2   +2   +2

When frame 6 happens, the Client applies the correction by rolling back to value 2.
And then replays its "SavedMoves", but only the ones that are newer than the correction.
The Correction usually comes with a communicated frame, so the Client knows that the correction is from it mispredicting Frame 2. So it re-applies Frames 3 to 6:

Correction from Frame 2  | Prediction from Frame  3 |  4 |  5 |  6
                     (2)                         +2   +2   +2   +2 = 10.

So it gets to 10 for the value on Frame 6, which will be in line again with the Server.

#

The Server will still receive the wrong values from frames 3 to 6, and will send corrections for those too, but the Client will handle them the same way, and since it's already in sync with the Server after the first correction, it will be invisible to the user, cause e.g. in Frame 7 it receive the correction that it should have been 4 and not 6 in Frame 3, replays the SavedMoves that are newer and will result on 12, which it already has anyway.

#

That's the very simple explanation of Prediction + Reconciliation.
That's why predicting state changes is really really really not easy. It doesn't even matter if you do them with spamming spacebar or anything else.

#

You get the same problem if the Client predicts moving an item from InventorySlot to InventorySlot in an RPG-like UI.

#

If it moves an item from slot 1 to 2 and then 2 to 3, and the Server didn't like the first move, then it will get that correction "later".

#

There is a bit more involved here, as the "frame number" that so nicely is in integer is usually a timestamp that has to be communicated back and forth with ping in mind.

#

And the SavedMoves are usually a lot more complex.

#

And there is in theory also a ClientRPC that Acknowledges the correct value, and not only the Correction one.

#

The SavedMoves exist on the CMC too. They hold all the info to replay the movement (which keys were pressed etc.).

#

Whenever a correction comes in, the Client usually deletes all of the SavedMoves that are older, cause those must have been correct, otherwise there would have been a correction for those earlier.

#

And it then applies the Correction and redoes the movement based on the still predicted SavedMoves.

#

So these SavedMoves usually have 1 of 3 states:

  1. Acknowledged: Server said this was predicted correctly, one way or another.
  2. Corrected: Server disagrees with what was predicted.
  3. Predicted: Client has done this and send it to the Server, but hasn't received any info about it being correct or not yet.
#

When replaying moves on a correction, it throws away everything older than the corrected SavedMoves (litterally deletes them, and counts them as Acknowledge). And replays all the ones that are still counting as 3.

#

And that's kinda all I can tell you about it without going really technical.

So if you next time wonder if you should predict in your interaction system, think about how difficult this is.

#

You can predict visual stuff, that doesn't alter state/gameplay. Like a gun shooting can be predicted, as long as the only part you predict is the bullet holes and hit effects, not the damage dealt.

#

It gets a bit complicated if you want the ammo change predicted. Ammo is probably the closest thing you can imagine that fits my exaple here. Just that it#s not +2 but -1 for each shot.

#

it talks about the same thing and has an interactive part at the end where you can toggle the different elements on.

#

For movement there is also Interplation and Smoothing coming up, cause the updates might not come in every frame, so you need to handle smoothing between the states to make it look good.