🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Movement Multiplayer Sync (Rewind/Replay) (Authorotative)

Started by
7 comments, last by hplus0603 9 years, 4 months ago

Hi everyone,

i'm having troubles syncing character movement(with charactercontrollers). i've read a lot of articles about how rewind/replay works for multiplayer games, and i think i basically understand the concept, but i run into a problem that i havent been able to solve for quite a while. My system works like that at the moment:

-) client sends movement input (e.g. timestamp: 100)
-) server stores the input + timestamp (100) in an array, and adds a "margin-value"(50) to the timestamp.
-) when the server-network time goes PAST the stored timestamp+margin value (150) the server accepts the received input and processes movement.

basically this works, yet, i do have a big problem: due to the fact that the player doesnt send inputs at a fixed timestep, the server never reaches the stored timestamp EXACTLY, but goes always past it, and therefore processes different inputs different long. those are just small values in the beginning, but they add up and become noticable quite fast.

any ideas/input/help on this would be REALLY awesome - i've been stuck on this for quite a while, and just can't manage to get it working!

PS: i try to avoid constant state-sync to reduce bandwidth. also i need the server to really "simulate" the inputs because of collisions, cheating, etc...

hopefully one of you network-gurus knows an answer .
cheers and thanks!

Advertisement
Why isn't your client simulation on a fixed timestep?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

You need to de-couple simulation, networking, and display time steps.
Your simulation should be a fixed time step, and you should count "steps" instead of "seconds" or "milliseconds" or whatever.
The easiest way to turn a variable-rate game loop into a fixed-rate game loop is to accumulate a measure of how much time has passed, and call the simulate function more than once if more time has passed than a step.

Check out the canonical game loop: http://www.mindcontrol.org/~hplus/graphics/game_loop.html

If you both simulate and render in the same function, you'll need to disentangle that through some refactoring.
enum Bool { True, False, FileNotFound };

Thanks a lot for your answers.

I made some progress on the syncing-issue. Currently the syncing works like that:
When the Client changes his Input:
-) Client sends Input to Server
-) Client starts recording how often this input is executed (so called „ticks“?)
When the Client then changes his Input again, he <b>sends the tick-count of the last input along with the new input</b>.
What happens on the Server-Side in the meanwhile is, that the server replays the input delayed (based on the client-ping) and stores the „ticks“ he already processed for the input as well. When the client now sends a new update that includes the maximum „ticks“ of the last input, <b>the server keeps executing the last input until the tick-count on the server reached the just received tick-count of the client</b>.
By doing this, i managed to match the client and the server perfectly. <b>De-sync only occurs from time to time when the player collides with static objects in the scene</b>. Does anybody know what could cause this problem? Maybe i should mention here, that i am using the Unity Engine. I noticed that spherical-colliders seem to be more of a problem than flat-colliders....
I thought that maybe the „Skin Width“ attribute of the character controller could have something to do with the imprecision, since (according to the documentation) it lets the character „penetrate“ into other colliders for a certain distance?
Also, do you guys see any problem with this approach? Could this be stable enough? It must be somehow possible to achieve the exact same outcome on server and client, at least when the latency is low and there is no packet loss?
any further help/ideas would be highly appreciated! :)
cheers and thanks!!
The fact that you're talking about "SkinWidth" makes me believe you're using some exisitng engine or middleware, but your post doesn't give any clues as to what that might be.
What physics engine are you using? Unity, Unreal, and other game engines use non-deterministic physics engines like PhysX, which cannot run a lock-step simulation for various implementation reasons.
enum Bool { True, False, FileNotFound };

Thanks for the reply, maybe it didnt came out clear enough from my last post.


Maybe i should mention here, that i am using the Unity Engine.

i'm using the unity engine with the character controller component. actually the character controller is quite deterministic, since you move it not by applying forces, but by giving him a vector to move to. yet, when collisions occur, a desync happens SOMETIMES - especially with spherical collisions.

Thanks for the reply, maybe it didnt came out clear enough from my last post.

Maybe i should mention here, that i am using the Unity Engine.

i'm using the unity engine with the character controller component. actually the character controller is quite deterministic, since you move it not by applying forces, but by giving him a vector to move to. yet, when collisions occur, a desync happens SOMETIMES - especially with spherical collisions.

Unity's physics is neither deterministic nor repeatable, and they never claim that it is.

Of course, neither are the physics engines of most other big engines. You'll have the same problem on UE4, on C4, and the rest.

Unity (and also UE4) does not directly support a rewind/replay mechanic. If you want that you'll need to implement it. People have done it, it can be done, but it is not supported out of the box.

As part of that, you won't be able to use the simulator's physics engine and character controllers in the way they were designed. You'll have to override their behavior from time to time, forcefully setting items into positions the engine wouldn't normally do, forcefully setting physics values to something other than their defaults, occasionally teleporting items to their right space, and otherwise not using the default values.

hi frob, thanks for your time!


You'll have to override their behavior from time to time, forcefully setting items into positions the engine wouldn't normally do, forcefully setting physics values to something other than their defaults, occasionally teleporting items to their right space, and otherwise not using the default values.

unfortunatly, thats what i thought. as for the approach i'm using right now:


When the Client changes his Input:
-) Client sends Input to Server
-) Client starts recording how often this input is executed (so called „ticks“?)

When the Client then changes his Input again, he sends the tick-count of the last input along with the new input.

What happens on the Server-Side in the meanwhile is, that the server replays the input delayed (based on the client-ping) and stores the „ticks“ he already processed for the input as well. When the client now sends a new update that includes the maximum „ticks“ of the last input, the server keeps executing the last input until the tick-count on the server reached the just received tick-count of the client.

do you think that this approach in general could work and be stable enough? or do you see some possible upcoming problems with it?

by following your advice and periodically correcting either the client or the server-position slightly, im quite convinced that i can sync the characters nicely... i'm just not sure if thats how it "should" be done, or if i'm missing something that could cause severe problems in the future?

I would suggest that you receive continual correction data from the server, and if the client is outside some small distance from the correction data, "snap" or "correct" the client to waht the server thinks. This means you send position/velocity data, not just input commands, from the server to each client. It also means people will mostly be in sync, with no risk of massive de-sync.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement