FPS Lower Than Tickrate

What would happen if the client was expected to send ticks at, say, 50hz, but his FPS was 30-40? Things would surely go wrong, right? Since the server is expecting ticks from each client at 50hz? But the client won’t be able to keep up

So what do?

In order not to depend on FPS, you need to send commands/positions to the server a fixed number of times, so send them to FixedUpdate because it does not depend on FPS and the number of calls can be adjusted even in runtime by changing the Time.fixedDeltaTime ( (1f / ticks) = delta between FixedUpdate calls )

And you need to send not 1 command for a fixed update, but several at once. For example, I do what I do, and so it should be, I buffer some commands, then pack them and send them over the network, on the server side I receive commands, buffer them, and in a fixed update we pull out one command and execute it.

I do send commands on client and receive on server in fixedupdate, but the second part of your answer confuses me. My client moves in fixedupdate, then at the end of fixedupdate, it sends out the UDP message with the movement command and whatever else he did that tick. How would I buffer multiple commands if I’m literally only allowing one command per fixedupdate?

1 Like

It is better to explain with a picture :slight_smile:

6976901--822893--upload_2021-3-26_14-55-35.png

Still a tad bit confused. Are you applying local movements outside of fixedupdate on the client? Sorry it’s 4am I’m tired, just trying to figure this out before I go to bed

or LocalPlayerCommand is where you’re applying the movement?

Edit: Your code looks very similar to mine, I just can’t figure out why you’re executing 2 commands per fixedupdate. If client/server are both ticking at the same rate in fixedupdate, and client only sends once per fixedupdate, why would you execute two commands on the server each tick? I guess the ping answer makes sense sort of, still trying to wrap my head around this, but that seems like it would pave way for speedhacks and such

Okay so yeah I did some further testing, set the tickrate to 60hz and locked my client at 30fps, while the server ran at 60fps. It seemed to do fine, except every like 5-10 seconds, the client would fall behind or get ahead of the server, and the client would need to reset his tick to become in sync with the server again. Does this sound right? Or should they stay in sync no matter what the FPS is?

The player movement is performed only in FixedUpdate and nowhere else.

Local Player Command is a structure containing information about the tick number, the keys pressed, and the player’s view
6977030--822899--upload_2021-3-26_15-17-49.png

The explanation about the two commands being executed per update is in the screenshot, but you’d better check it out in person to understand

Therefore, I buffer the commands even on the server side, because if the server executes all the commands at once when the client sent them, the movement will be performed instantly, regardless of the server’s ticking speed. And so each update the server takes commands from the buffer and executes them smoothly preventing client hacks.

In theory, yes, but I understand you haven’t implemented player prediction and reconciliation yet?

And in general, the server should not care what happens to the client because the client can always cheat and the server can only trust the client’s commands.

I have both prediction and reconciliation fully implemented. This seems to be the last roadblock for me.

How my ticks work is, when client joins, he predicts which tick server is on based on RTT, and then doesn’t do it anymore. Instead, the server tells the client to either increase/decrease the tick until he’s within an acceptable range.

If the server receives ticks from the past, they get dropped, which is why I’m uneasy about this, since it seems that if the player’s FPS is lower than tickrate, the client will inevitably fall behind and have to re-sync with the server every few seconds, but when that happens, the server won’t process the tick, because it’s in the “past”, and it might contain important information like “shoot your gun” and the server will just ignore it because it’s in the past.

It would be desirable to synchronize the global tick of the game more often, and also send the current delta of the rendered frame together with the team, at least this is done in quake3 → half-life - > counter-strike → source engine as far as I know

I’m not sure if I can synchronize more often, since the client is sort of at the “perfect tick” until he actually does fall behind, then the server tells him that he’s behind and that he needs to increase his tick by 1. This takes about 1 tick to occur so it’s sort of perfect in my opinion. I’m not sure if I could make it happen any faster.

Have you tested about how often your clients fall behind?

Better do it like this … Simulate the server tick simply by adding it as the server does … When receiving new tick numbers, just correct them.

In any case, the client will lag behind, even if it is connected to the local server, I have an average of 2-3 ticks left, it all depends on the frequency of network updates

Note that FixedUpdate doesn’t actually run at a fixed rate. It doesn’t interrupt processing already happening which may be taking too long, and shares the same main thread with the rest of your MonoBehaviour code. If you have a frame update take an exceptionally long time, you might end up getting FixedUpdate called back to back a dozen times before the next frame to catch up. So over a long period of time a 50hz FixedUpdate should average out to that rate, but it won’t actually run exactly 50 times per second. You shouldn’t design your code to require FixedUpdate to be actually running at its specified rate.

So do you have a suggestion as to my original problem? I’m honestly not sure at all how I’d do this, there’s not really a lot of articles online about tick synchronization etc