0
\$\begingroup\$

I am trying to implement a stamina system for my mmo. In few words, I computed everything on the server that then notifies the client. On the client I have a stamina bar that shows the stamina amount. My problem is that the values on the server and the one on the client often go out of synch because of network delay and that causes odd behaviours on the bar.

EDIT 2:

There are things that can affect the regeneration/depletion of a stat. My architecture is server authoritative and the server is in charge of handling the flat changes and the changes in the delta amount per second, that's a constraint.

END EDIT 2.

The communication scheme is between Client (C) and server (S) is the following:

C: me want use stamina

S: ok your stamina is X descreasing at Y/s

C: set stamina to X and animate the bar accordingly to Y/s

every 1 sec after this initial handshake

S: this is your stamina X_i and it is decreasing at Y_i/s

C: use information to animate the stamina bar coherently yet in a way that resembles linearity

as soon as S knows stamina is 0

S: your stamina is 0

C: set stamina bar to 0

Due to loss of synch, the value 0 may appear when the real value is not really 0. The client holds a float amount that is decreased/increased and ceiled to int at every frame to be displayed.

Options I have already tried:

-Whenever the server updates you, force its value: causes jumpy behaviours and doesn't achieve fake linearity

-Whenever the server updates you, calculate the client regen/loss factor as (server amount + delta amount per second) - client amount. Causes the most overshots (client value going to zero when it should not be 0).

-Whenever the server updates you and you find a delta between the server value and the client value, run a coroutine that adds/subtracts over two or three frames that delta to the client value. Still too jumpy and sometimes the error is so big it makes the bar make huge leaps.

Language c#, network library photon, engine unity.

EDIT 1:

My partial solution has been:

The server sends you an update including a duration and a target value predicted for when that duration has elapsed. The client at each update starts a tweener (DOTween library) from it's current value to the target value over that duration. I had the issue that sometimes the client would display 0 when it wasn't the case on the server due to network delay that didn't stop the client in time, and if you reach 0 in my case I have to perform a cooldown wait that wasn't happening being the command to start the cooldown issued by the server. So what I've done has been to make the server accept the 0 from client when it was the case. It seems to work but on changes on the delta value per second, sometimes it might slow down or get quicker in a way that it is noticeable, sometimes.

END EDIT 1.

EDIT 3:

Additional tweak that makes it look a little better.

When the event that starts/stops consuming the stat occurs in the client, I stop the stat bar and wait for server update.

END EDIT 3.

\$\endgroup\$
1
\$\begingroup\$

What I suggest you do is to simulate the change client-side, using the same calculation on server-side, and send request to the server at the same time, using a timestamp to know what value it is. Then, when the server answers, with its own, checked value, compare it to the value you had computed at that timestamp, and if it is incorrect, change it.

You can perform the check only when the stamina has stopped being used, to avoid having multiple check requests, and to avoid to have to store their values.

== EDIT ==

You're effectively right about the stamina being in constant use. There is indeed no point in making the checks when it stops going down/up, so you can simply make checks something like every 30-40-50 ms, depending on the rate you send packets to server.

== END EDIT ==

As it is very improbable that the results are different (if you use the same calculation...), there would be therefore no jumpy behaviours, except if someone modifies its own client (but then it's his fault if he makes everything bug out :D)

\$\endgroup\$
  • \$\begingroup\$ Define timestamp, we are talking about two different machines and their system time is 99% out of synch. If the client finds out its value is incorrect there is going to be a jump anyway. Plus you are suggesting to check only when stamina is not being use, but then the value of stamina is constant and there would be no synchronization issues. Either I didn't understand anything you've written or this doesn't seem to resolve my problem sadly. I have edited the question with a partial solution if you want to check it out. \$\endgroup\$ – Alakanu Mar 16 '17 at 12:51
  • \$\begingroup\$ You could make your own timestamp. Have both server and client counting up in ticks and every now and again sync the client to server's ticks. \$\endgroup\$ – lozzajp Mar 16 '17 at 14:17
  • \$\begingroup\$ Ok, that makes sense. But if the server and the client are doing the same calculation, what is the point of having the calculation happen on the server at all? I'm fairly new to networking and games so pardon me if you think I'm asking stupid questions. \$\endgroup\$ – Alakanu Mar 16 '17 at 14:27
  • 1
    \$\begingroup\$ @lozzajp's solution is great. What I also meant by timestamp is that the client sends a packet with its own timestamp, the server just makes the checks, but doesn't change it, which makes it the same for the client receiving it. To answer your second question (btw there's no stupid question ;), you have to make the calculation twice to obey the first rule in programming : Never trust user input. Here, the user input is the clien'ts one. If the client's code get modified (you never know), the server has to check it to display the right content at least to other players. Here's why :) \$\endgroup\$ – cocosushi Mar 16 '17 at 20:44
  • \$\begingroup\$ The point is, a lot of things can affect the regeneration/depletion of a stat. My architecture is server authoritative and the server is in charge of handling the flat changes and the changes in the delta amount per second. Your solution to me sounds a lot like: change architecture and make the client do the calculation, which goes against one of my constraints. Is the solution you've proposed the standard one? \$\endgroup\$ – Alakanu Mar 17 '17 at 10:17

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.