5
\$\begingroup\$

I have a tick-based Multiplayer-RTS game: Client sends action to server at Frame n which will be broadcasted to all clients and executed at Frame n+x (x depends on network latency of slowest player)

I'm unsure how to hide this latency in GUI dialogs. E.g. a value that is adjustable with a slider between 0 and 100. The current solution simply fills a "virtual" value with the actual value at game start and always operates on that value. It is highly unlikely that a change command for this does not get executed (TCP, unconditional change execution) but it is possible (packet sent and ignored during pause)

I also wanted to keep that value local to the dialog and not store it somewhere else. Start idea: Get the current value on dialog open and use this while the dialog is open. Problems: Maybe the value was changed before (packet sent, but not executed) in which case the change does not get displayed. An idea I have is storing the last value I got and update the slider if that value has changed. Maybe only when the sliders position itself did not change (by the user). However then after the update it will detect that change and change it again which leads to a change of twice the amount requested. I could also update the "last value" when the packet is sent but that will cause the (time-based) update to change it back till the actual change is made which might even result in a loop of back-and-forth changes. How can I keep the GUI and game in sync but allow visual changes? Seems I'm missing a little piece here...

Another part where this is even worse is with other kinds of dialogs. E.g. an ordering dialog (put some entities in a specific order) or a number input that is used to request producing the specified amount, hence the game may also decrease this, not only the packet. I could not find any guides or solution besides "use inter/extrapolation" which is not applicable here. How do others do this?

Edit: Maybe a little example for the strategy I though off with its flaws:
A) OK

  • Value=4
  • Open Dialog -> Show Value=4
  • User Changes Value to 6 (Slider position change)-> Sent to server
  • Server sends "6" -> Slider changes to "6" (no change as it already is)

B) OK

  • Same as A) but dialog closed before receiving from server
  • Dialog reopened -> Show Value=4
  • Server sends "6" -> Slider changes to "6"

C)

  • Value=4
  • Open Dialog -> Show Value=4
  • User Changes Value to 6 (Slider position change)-> Sent to server
  • User Changes Value to 7 -> Sent to server
  • Server sends "6" -> Slider changes to 6 BAD
  • Server sends "7" -> Slider changes to 7 => OK, but strange and may cause feedback loops

D)

  • Value=4, change to 6 requested
  • Open Dialog -> Show Value=4 (6 was stored in the old dialog)
  • User Changes Value to 7 -> Sent to server
  • Server sends "6" -> Slider changes to 6 BAD
  • Server sends "7" -> Slider changes to 7 => OK, but strange and may cause feedback loops

So on one hand I only want to show visual values (and store them between dialogs) but on the other hand I want to keep them in sync with the actual value. So only solution here seems to be to persistently store the visual value and make sure, each update will eventually be executed. But I'm still clueless what to do with values that can be changed by the came (e.g. production orders that decrease once they are processed)

\$\endgroup\$
  • \$\begingroup\$ That dialog should just watch the value and not store it locally. Doing so will forevermore tie the visual handling to the data itself, making it very hard to manage. In general, visual representation should be separate from data to solve all the problems you've noted already. I don't have much time right now, others will have to add real answers covering topics like MVC that deal with this. \$\endgroup\$ – Patrick Hughes May 21 '16 at 19:31
  • \$\begingroup\$ I have a MVC concept: Model: current value stored with the player, View: dialog that accesses it, Controller: message handler, that updates the value. The model sends a notification when the value has changed and the view sends a message when a change is requested (slider position changed). The problem here is the compensation between the visual change and the actual change. If you change the slider position, you expect it at the new position immediately. And not have the whole network and command delay between that. So I need at least 2 values: Current real value and visual value. \$\endgroup\$ – Flamefire May 22 '16 at 10:50
1
\$\begingroup\$

First, you need to assure that you get your commands to be more stable than "highly unlikely." Why is 'pause' state ignoring remote updates?

Second, often quoted networking reference contains examples of handling latency on both server and client, plus dependable communications. I think that ideas on that page may make you rethink your "turn n+1" concept because server time is quite fluid and not lockstep to the clients at all.

Using ideas from that networking reference your GUI latency will mostly disappear. However, there will be times when the server invalidates your choice and that means that your GUI and game will have to adjust to the new, blessed value, but that's expected in an online, real time title.

In your particular case I have a suggestion: I think that you're right about wanting two variables to track this visual. The first variable is the server blessed setting which is updated from messages received. The second variable is the locally requested value, what the player wants this to be.

In your GUI you show the real value so the player always knows what it is and then you also show the requested value (as an overlay, a different icon or color, whatever works for your look) and when the real value is received from the server the overlay disappears.

In your logic you will work from the requested value. Also in your logic if the server invalidates your setting you will need to be able to go back to that frame and re-run the game simulation up to that point. This is also covered in the reference.

The blessed value from the server is a one-way communication and will fix your ping-pong scenario and/or double movement. At the expense of extra logic to replay unaccepted requests.

\$\endgroup\$
  • \$\begingroup\$ The command will get through the server. But there is a clientside protection that no command is sent during pause. Probably should be enforced by the server. This is to prevent advantages by actions during pause. So GUI might sent the command, but the client will swallow it before sending it to the server The reference also says that lockstep models are still ok for RTS and I can't change the entire concept of the (already existing game) Rerunning frames is also impossible with the current design :( \$\endgroup\$ – Flamefire May 22 '16 at 20:50
  • \$\begingroup\$ The double-color idea is good. But the problem remains: Which value does the user change, when clicking up/down? Which value is then requested? And where/when is it stored/updated? There might be updates pending when the dialog is opened and I don't really want to store it with the model as this is a purely visual value with no real model relation (unused by the model) \$\endgroup\$ – Flamefire May 22 '16 at 20:57
  • \$\begingroup\$ If you're going with lockstep and server authoritative, then you have no alternative but to change the local requested value temporarily in the dialog and then only act on the real value when you hear it from the server. You will have to expect delay. Based on your revised question there will be no feedback loops but there will possibly be multiple updates from the server with different numbers. Think of it like an air conditioner thermostat - you can say you want 68deg and change that multiple times but the temperature will not get there right away - you have to wait. \$\endgroup\$ – Patrick Hughes May 22 '16 at 21:38
0
\$\begingroup\$

I think your mental model of how to handle state updates is fundamentally flawed. @Patrick Hughes suggestion of Gaffer on Games' Networking Articles are really a good primer on the subject.

When working with real world networks you need to overcome four problems:

  • latency
  • jitter
  • packet loss
  • out of order delivery

Now you though about latency, which just the time it takes for packets to go from application to application. Jitter is changes in latency. Packet loss is when packets simply don't get delivered. And finally out of order delivery is when one packet gets delivered after the next one in the sequence.

It appears you are using TCP, where you man not need to handle the two later ones, but that just results in more jitter. (Not on the wire, but the app, since TCP masks the issues by waiting.) In a client server environment these network effect will ensure that your clients will run out of sync quickly. That is why the replay approach never really worked out.

The dominant approach is the send commands to server and send world state client. This allows for two things, first the server can take each command and execute it, which ensure a consistent state (for the server). Secondly, it allows client that are aware they are significantly lagging behind to just skip forward to latest world state and just ignore everything that happened in between.

As to your specific problem, as long as both users see the same value at roughly the same time all is okish. If two users are trying to change one value at the same time, well that is a higher problem in your design. Imagine a system with a perfect network, the fundamental problem will not go away, since Alice will set to 7 and Bob to 6. When Alice sees the value being 6, but not 7, she will be irritated and set it back to 7. But in turn Bob will be surprised the value went to 6...

\$\endgroup\$

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.