Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Clients and host desynchronizing when not running the game.

Discussion in 'Netcode for GameObjects' started by GuirieSanchez, Nov 25, 2022.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    Let me provide a little bit of background on what I'm experiencing.

    I'm testing a turn-based board game on different devices (a pc (client), a phone (client), and an iPad (host)) and it works correctly if the games are running without any stop (as long as they keep synchronizing). Each player has a timer for their turn, and some buttons to perform some actions. Here's the key thing: Only one action is allowed per turn. When the player chooses an action, the timer stops, it performs the action, and the next player gets his turn.

    Now, the problem comes when the host (iPad) opens another app and the game runs in the background, or better said, stalls until the app/game is back on screen. During this time, the timer of the current player's turn stops, while it continues on the clients' game (phone and PC, in this case). So, whenever the iPad comes back, timers will not be synchronized across devices, which might be a big problem.

    Another issue is that, when the host is not running the game, a player can hit a button to perform an action, but nothing will happen since the
    [ServerRpc (RequireOwnership = false)] function
    won't be performed until the host comes back. This is fine; however, if the client keeps pressing the button multiple times (let's say 10 times), when the host comes back, the
    [ServerRpc (RequireOwnership = false)] function
    will run 10 times instantly, breaking the game since only one action is allowed per turn.

    I hope this kind of explains the problem.

    A possible solution that came to my mind would be to let the clients' game freeze until the host comes back, so that the timer will keep synchronized and clients won't be able to accumulate actions. A drawback will be that, if any client (for instance, the phone) stops running the game for a couple of minutes and then comes back, it will not be synchronized with the rest.

    Any idea or suggestion is highly appreciated.
     
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    There is a Run in Background option you could try under Player Settings->Resolution and Presentation. For timing I have the server dictate the turn time and sends a message to the clients every tick.

    You need to have checks for this on both client and server. You could disable the button or however it's setup on the client after they select their turn action but they could still find ways around it. On the server flag when a player has taken their turn so it can reject any following actions until its their turn again.
     
    RikuTheFuffs and GuirieSanchez like this.
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    Thank you! your answer is very instructive.

    I think this would be ideal.
    The reason I didn't implement it is because I couldn't run a coroutine with a [ClientRpc], so what I did is to fire off a coroutine from another function that is a [ClientRpc]: this way each client runs the ClientRpc function calling a coroutine by their own but at the same time. This could cause some problems, such as the one I mentioned, where timers will get desynched as people get their apps/games in the background, creating a total mess throughout.

    So yes, I want the Server/Host to be able to set the timer back to the clients on each tick. This way I can make sure everyone will be synched.

    How would you go about and do this? In the timer coroutine, I'm updating text, images, filled bars, and a bunch of other stuff. As an idea (I might be totally off), maybe I can trigger the coroutine on the server/host, and for each tick, I call a [ClientRpc] function that updates all the parameters (text, UI, etc...) with the passed in parameters from the coroutine. Does it make some sense? Or were you rather thinking of another method?

    PS 1: I wonder what would happen if the timer reaches 0 and the server calls a ClientRpc when a client is afk. Will they run everything at once as soon as they get back in order to keep up with the rest?

    PS 2: Regarding the buttons, I think your solution is perfect. I can disable the buttons on the client side as soon as they press them, and let the Server/Host update the information as soon as he's back in the game.

    Btw (as a side question), when clients press a button I call a [ServerRpc(RequiereOwnership = false)] that then calls a [ClientRpc] to update the action on everyone's game. This works fine, but I noticed there's a delay since the button is clicked until the action is performed, mainly because the info needs to travel to the server and come back. Is there a solution to this? Considering that is the host who gotta send the info to the rest, not only to the one who performed the action.
     
    Last edited: Nov 26, 2022
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    It's difficult to answer as it sounds like we have use different approaches in our setups. I'm running as server only so no host to to worry about, the players take their turns at the same time, and I'm not using coroutines at all. I lied about sending a message on each tick, I had a look at the code and it just updates a network variable. I'm using network variables everywhere including for UI updates, rpc messages are used mainly for sending player actions to the server.

    Timed events are handled in a manager, if I want something timed I pass in the details of what to call per tick, the call interval, number of calls etc, and on each Update a check is made to see if enough time has passed to make the next call.

    A player could choose not to take an action, but I don't know how your game works. If they have to take an action but the timer runs out the server could do that for them.

    Again I don't know your game, if you're withholding values until all players have taken their turn then no there's not really much you can do about it.
     
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Not sure if "run in background" works on iOS / mobile devices. For iOS this is different, see enable custom background behaviours and the list doesn't mention networking. Quite possible Netcode and in general Unity's Update() etc cannot be run in the background.

    Hosting on an iPad has its limitations, and I would generally advise to have a dedicated server for such scenarios. Otherwise inform your users (the host) that the app must not be put in background otherwise all connections will be dropped (and I would force that to avoid any issues on client-side).
     
  6. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    Will sending ClientRpcs to update the info on each tick be expensive as compared to running a coroutine on each client?
     
  7. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    It should be trivial but it depends on the amount of data you're sending. Really you only want to send the time remaining for that player's turn. If you're sending more than that then only send values that have changed. This is why I use network variables as they're a convenient way to do this.
     
    Last edited: Nov 26, 2022
  8. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    I checked the documentation and the links and examples about network variables are broken, so I'm not sure how to implement them correctly. Therefore, I'm going to try using a ClientRpc to update essential info each tick, and see how it goes. Thank you

    I see. The game is meant to be used mostly on phones and tablets, so I think there's no other way around it more than advise the user. Unless I could be able to freeze client's games until the host is back.
     
  9. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    GuirieSanchez likes this.
  10. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    Thank you for the link! I was checking an outdated documentation page since I'm not able to update to Netcode 1.1.0 (even though I'm using the latest unity version), but I think it still works on mine (1.0.2).


    PS: do you think
    void OnApplicationFocus(bool pauseStatus)
    (as seen here) is a good way to detect if a client's app is in the background, and as soon as he comes back, perform a custom function to get all the info necessary from the server/host in order to be up to date and keep up with the rest?
     
  11. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    GuirieSanchez likes this.
  12. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    406
    I see, I'll come back and share if I find any suitable alternative. Thanks for the help! I'll mark the thread as resolved.