-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explanation of the network synchronization #284
Comments
Hello! From what I know, fighting games are p2p which is where the whole complexity comes from, whereas Hypersomnia is a completely client-server architecture. I've been asked the same question by a fighting game enthusiast and they made me realize I'm doing something called GGPO:
This is exactly how it works in Hypersomnia but it is the server that governs what inputs were applied to every simulation step, not the clients. I don't synchronize clocks at all - it is a self-correcting system. I don't even use timestamps for messages. There is simply a bidirectional stream of completely reliable messages over UDP, the client sends its inputs every step @ 60Hz (even if there were none, it just says "empty input"). The client begins simluating its own world forward the moment it connects, and populates the "predicted inputs (steps)" vector until the server updates start to arrive. Server, within every step update packet (@ 60Hz too) says how many steps worth of client inputs have been accepted. The client pops that many steps from the front of the vector of predicted inputs, then re-creates the "predicted" (rendered) game world by re-applying the remaining predicted inputs on the "referential" (off-screen) server world (which is deterministically simulated from the server commands, and it contains the always correct/certain server state. It is only ever simulated forward the moment that server updates arrive). Thus the amount of "prediction" - i.e. how much the game nudges into the future when displaying the game world - is always proportional to the apparent network lag, and is 100% adaptive to fluctuations in latency. |
Hi! Thank you! |
Yes.
Yes, the server does too, although my explanation focused on the client-side, and yes, clients do make this assumption as well.
No, it is the clients who do it. The server never rolls back any state, it mercilessly marches forward in time and accepts clients' inputs as they are received, immediately to the soonest simulation step.
No, the server does not send any predicted input. It also does not send any position, velocity or rotation data except when connection is initialized (clients need starting data to deterministically simulate from). Later it just broadcasts the "canonical" inputs of all players it decided to apply to each simulation step. |
I think I understand now! The server just simulates every client's input on the next simulation tick. So it doesn't really care when the client sent it. Itt relays the clients inputs back to the clients. Where the clients can then perform rollback and correct their own simulation of the game? |
Correct with one exception - the server never waits. It always simulates at a steady rate. It applies client inputs as they arrive from the network, so it is in everyone's best interest to send them as fast as possible, at as regular intervals as possible. There of course is jitter - two steps worth of client commands could arrive in a single call to Edit: after your edit, it's of course correct. |
Also, like I mentioned wrt. clock synchronization: with each canonical step relayed from server to the client, the server includes the number of client "input steps" that have been successfully "applied" - so it could be "5" after the merge of jitter buffer, always "1" under normal conditions, and "0" for the duration of a lag spike. The client then pops that many steps from its local prediction queue. This is how the system self-corrects for lag fluctuations. |
Thank you for this, it’s such a huge resource for the gamedev community!
According to your README, the players merely send inputs and then replication or local simulation is done by each individual player. This is similar to how fighting games work. Are you performing roll-back and clock synchronization as well as fixed frame updates to ensure synchronized tick rates? I love C but I have difficult time understanding C++ :(
The text was updated successfully, but these errors were encountered: