I want my code to run/perform the same on different Unity clients. I don't know if this is hard to achieve.

Here hare my problems:

  • The enemies movement: I have a pathfinding that should always give the same results on server and on clients. Because I use the same pathfinding algorithm on server and client, and the position the algorithm chooses is synchronized with a network transform. But it does not give the same results on client and on server.

  • Bullets make enemies die over all clients: I have a script with OnTriggerEnter2D(Collider2D collision) that never gets called at the same moment on clients and server. The clients might miss touching a bullet on an enemy on server but not on a client. So in server enemies died and on client they have not. How to synchronize OnTriggerEnter2D() ?

I will update the question to improve it with more examples when I find them.

  • 1
    \$\begingroup\$ 1) what the server returns should be the result. You might want to have the pathfinding on the client for a prediction, but still take the server result as absolute. They should not differ by much in the first way and you might be able to smooth over the difference depending on your game. 2) it can't be called right at the same moment since there is some latency between client and server. Again if server says it hit, it doesn't matter if it really did on client, you need to destroy it on the client. Else modified clients are causing mayhem \$\endgroup\$ – Zibelas Dec 25 '20 at 19:56
  • \$\begingroup\$ I will try to implement 2) in my game. I just need to destroy it in the client and call the server. And the server sends back the verification that it has been destroyed. The problem that I have now is that the client is hitting an enemy but the server does not detect the hit (maybe because of delay and the bullet is offset from the enemy) so it does not send to the client that the enemy is hit. \$\endgroup\$ – kyu Dec 25 '20 at 20:46

What you're describing is "determinism" - this is the situation where two instances of your program can independently arrive at the same outcome, consistently, given the same input.

Determinism will not solve your problem here, because you do not have the same input.

The copy of your game running on the player's computer (the client) knows what move button they pressed at time 15:01.000. The copy running on the server does not find out about that action until one packet latency later - maybe 15:01.033, a full frame later.

From the client's perspective, you dodged the bullet just in time. From the server's perspective, you were a frame too late and the bullet hit you. Those are both correct simulation results for the input they were given. So even if your simulation rules are perfectly deterministic, you can get divergence: give different input, and you will get different results.

The simplest way to solve this is to decide by fiat that one of these versions of events will be honoured as the authoritative truth, and the other will be regarded as a temporary mirage, that quickly gets corrected to match this truth. In a client-server game, we'll usually choose the server to be this authority (because it can make it more difficult for players to cheat), and we call it an "authoritative server" model.

Here, the client's simulation result is considered to be a "prediction" - visualizing for the player what the results of their action should probably be, while we wait for confirmation from the server. The server independently simulates the results of the actions as it receives player input, and sends updates describing the outcomes.

When a client receives an update from the server that differs from its version of events, it applies a correction: blending (or in some cases, snapping) its local state to match the server's.

You can sometimes work around this to a degree with some time travel: when the server learns about some new player input, it estimates the time that input was sent, and rewinds the game simulation to that moment, applies the input, then re-simulates forward to find out what happened. This maintains one "consensus timeline" that more closely matches each player's view of the sequence of events in the game - especially relating to their own actions. But it can still run into surprises, where an action by a remote player that the local client doesn't know about yet changes the outcome of an event they've already predicted and displayed - so you still need to correct to the authoritative version of events in this case.

For your specific situations:

  • The client can perform the pathfinding query, then choose the first waypoint on the path and send that to the server so that it and all connected clients can animate the entity moving to that point. Keep sending the next waypoint each time you reach the previous one. You may need to re-path periodically if you learn about a new obstacle partway through pathing.

  • OnTriggerEnter with a bullet can trigger an animation (eg. a flinch) and sound effect so that the player gets immediate visceral feedback about the collision - but it should not be responsible for damaging or killing the entity on the client.

    Instead, the server should be responsible for adjudicating whether a lethal hit occurred. When it does, it can send an "Entity killed" message to all connected clients, who can respond by transitioning that entity into its death state. The animation we started playing locally can help disguise the delay between the hit and the confirmation of the kill.

    If no confirmation arrives, the entity can shrug off the illusory hit ("it just grazed me!") and continue unharmed. You may need to add a little extra showmanship though when killing an entity with a bullet that missed in your local simulation. Some extra particle effects can help sell the cause of death so it doesn't look like the entity just spontaneously croaked.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.