0
\$\begingroup\$

The problem: Let's say we want to create top down MMORPG in browser, and we started with implementing movement of your character. Okay, so how it works?

  • Character gets inputs and compares if they are the same with the old frame, if they are different it sends to the server that inputs change. So if you press D and release after 0.1sec it will send 2 packets, when you pressed D and when you released it.

So far so good, but now on the server. How do we sync the server with a client? Okay

2 Options

  1. Fixed timestep, we just process inputs in a while loop which will run at 60fps so it will be using the same fixed timestep as a client, but we need somehow fix the issue when you don't receive inputs.
  2. We just store input A with client Tick, and wait for next input, if we receive next input we know it's a start so we know how many times we should play input A. Using B.clientTick - A.clientTick we know how many times we should process this input, but what if client modifies the clientTick on input B? perhaps 1) is better in this case.

The problem, we never know in advance when player releases D and in networking behavior it is not guaranteed that if the client press D and after 0.1sec release it, it will be same for server, in this case server will move more or less, if you receive release packet after 0.15sec, you can play one more frame and your positions on client and server will be not synced.

What are possible solutions? This is why i am asking you, i can't find proper solution.

Solutions i know:

  1. Run client at fixed timestep (60fps) and send 60 packets a sec (or as fast as possible). This is not the best especially on TCP connection. This is why i want send only packets when player inputs change.

  2. All packets the client send can play at max 3 times and client will send packet if you change inputs or every 3rd frame (considering it is in fixed timestep). Server will store last 3 inputs in buffer so if let's say you receive: CTick: 0 - Input D - Server Tick 0 CTick: 3 - Input D - Server Tick 3 CTick: 5 - Release D - Server Tick 6 (it will must rewind one frame because Release D was released after 2 frames from last packet not 3 frames)

But problem can be here Tick: 0 - Input D - Serve Tick 0 Tick: 3 - Input D - Server Tick 4 (Now we have gap and one frame is not played on server)

  • To overcome this problem we can replay "last received input" and if we get same input next, we can just throw it. If we get different input we rewind the position

I feel like whole this implementation is quite more complex than it should be and I am not sure if this is proper solution, what are your thoughts or suggestions?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Is it safe to assume you've read up on standard approaches to this problem including reconciliation (when the server sends a state update that differs from what the client simulated locally, the client interpolates to the server's version to maintain consistency) and rollback (if an input received late tells you something should have been different in the past, you rewind the game state, apply the correction, and fast forward to generate the new current state based on the updated history)? \$\endgroup\$
    – DMGregory
    Jan 16, 2023 at 23:58
  • \$\begingroup\$ @DMGregory Thanks for your reply, yes I read standard approaches, and most of them are based on fixed timestep with sending inputs as fast as possible, which I want to avoid if possible. The rollback you described is what i was thinking about, let's say i will send inputs with a max difference of 4 frames and if the server get no input it will replay the last one and when it receives the input it should have, it will compare last moves and if they are not correct, it will rewind them with correct input. This should work unless the player has high latency/packet losses. Is it correct? \$\endgroup\$
    – saqirmdev
    Jan 17, 2023 at 8:16
  • 1
    \$\begingroup\$ You don't strictly need the max difference. The server can repeat inputs eagerly, and roll back any time a late "stop/change input" message tells it that it repeated an input too many times. \$\endgroup\$
    – DMGregory
    Jan 17, 2023 at 12:07
  • \$\begingroup\$ @DMGregory Thanks I understand now! Perhaps write your output into an answer so i can select is as answer. \$\endgroup\$
    – saqirmdev
    Jan 17, 2023 at 12:58
  • \$\begingroup\$ I'll confess I don't have a good sense of what the issue was, so I don't think I'd make a very good answer. If the comments above helped you though, you would be welcome to write your own answer explaining how you've solved the problem based on that..You can accept your own answer two days after the question is posted. \$\endgroup\$
    – DMGregory
    Jan 17, 2023 at 13:23

1 Answer 1

0
\$\begingroup\$

Thanks to @DMGregory who help me to solve this issue.

The final approach

  • The client sends inputs when they change and current tick/Ack
  • Server receives input and stores it in a buffer
  • In server-tick, the server will replay the last input received until new input is received and increase the simulated tick by 1
  • When new input is received, it compares the client tick/ack of new input and if it is lower or higher than the simulated tick, the server will instantly play missing frames or rewind the last X moves (should be limited to a certain value) and play them with the received input. If the server must rewind more than 4 frames, it will not do so and it will force the client to update its position smoothly (Also known as rubberbanding?)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .