Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved Host Migration Solution

Discussion in 'Netcode for GameObjects' started by NelsonChristensen, Mar 5, 2022.

  1. NelsonChristensen

    NelsonChristensen

    Joined:
    Nov 1, 2020
    Posts:
    2
    Is there any host migration solution currently available for netcode for gameObjects + Unity Lobby Service + Unity Relay Service? I searched the forums and github and the only thing I found was this: https://forum.unity.com/threads/host-migration-options.1123585/#post-7313122 and some docs from old UNET solution (https://docs.unity3d.com/2022.1/Documentation/Manual/UNetHostMigration.html). If anyone has any resources on how to build a robust host migration system I would greatly appreciate it. For example how would I restore the state on all the network objects since they get removed when the session ends?
     
    nico_st_29 likes this.
  2. nico_st_29

    nico_st_29

    Joined:
    Mar 14, 2020
    Posts:
    68
    I'd be interested in the above as well. Anyone has an example maybe?
     
  3. NelsonChristensen

    NelsonChristensen

    Joined:
    Nov 1, 2020
    Posts:
    2
    EDIT: I've added a more detailed description in the spoiler at the bottom.

    I managed to create my own solution. I'm using Unity Lobby Service which makes it a bit easier since players can share data outside of the game session itself. But essentially whenever the host disconnects:
    1. Pause the game for all clients
    2. Start polling the Lobby every x seconds for whoever the new host becomes (it automatically selects a new host)
    3. New Host: Start a new game session and update the Relay Join Code in the Lobby
    4. Clients: Poll the Lobby for changes made to the Relay Join Code. Whenever it has been updated, join lobby
    5. Restore Player/Game States
    I haven't really tested it out that much yet on a larger scale, but from my small tests it seems to do the trick.

    EDIT: Added more detailed description below:
    There are a few problems that needs to be solved when the host leaves:
    • All clients needs to collectively be informed of who will be the new host
    • The new host needs to start and new lobby and inform the remaining clients of the new relay joinCode
    • The remaining clients needs to automatically connect to the new joinCode
    • Session state needs to be restored

    So to tackle the first problem I created a networkedList called
    _clientHostOrder
    where the clients are just listed in an arbitrary order. The important part here is that since its a networked list all clients will know the exact order.
    This order determines which client that should first attempt to host the new game whenever the host leaves.
    Of course this list needs to be updated when a new player joins/leaves.

    The next problem is that we need to inform the other clients when the new game has started, and what the relay joinCode is. This can be done through the lobby system, because even if the host disconnects from the relay(leaves the game) the lobby will still be up. One problem here is only the lobby host can actually set lobby data, but all players can set data on their own lobby PlayerObject, and you can set all of this to be visible by all other lobby members.
    So the new host will add the relay join code to their own playerData, and all clients in the lobby will check the other players PlayerData for updates every X seconds (I think there are some rate limits, so you can't poll too often, but if you wait for like 2 seconds before initial check the new host has hopefully already got the new relay server up and running)

    One thing that I've implemented here is also so that if more than x seconds passes without a new game being up and running the next person in the
    _clientHostOrder
    will attempt to host the game instead, because the first person that was supposed to host might have left as well.

    Restoring session state is a bit tricky, but you can use the
    NetworkManager.Singleton.OnClientDisconnectCallback
    event to listen for disconnects, and if the disconnect clientId == 0 it means that the host is disconnecting. So at that point we need to store the whole game state somewhere persistent, since our Player Prefabs will get destroyed.
    The complexity of this will vary a lot based on the game you are making, but I'm pretty much only storing the local player Position, Rotation, Health, PlayerState(Dead, Alive, Downed, etc) and score.
    The game I'm working on uses serverTime for a lot of syncing, so I also store the serverTime here (which I can then advance back to in the new game session by calling
    NetworkManager.NetworkTimeSystem.Advance(_serverTimeAtDisconnection)
    )

    But if you have enemies with networkTransforms or anything similar those would need to be stored as well.

    Then whenever the new host has recreated the new game session it is responsible for restoring the game state,
    and whenever a player reconnects that player is responsible for restoring its own player state.

    That is pretty much the scope of what the game is doing. Some other minor details:
    • Whenever the connection is initially lost we change the Time.timeScale = 0 for all clients so the game is paused and we display a UI prompt on screen with "Host migration" or something similar
    • The lobby is a bit delayed, so the original host will probably be removed from the lobby maybe 20sec after they have actually left. When that happens a new lobby host is assigned, this might not actually be the game host, so I've setup so all clients check the Lobby every 30sec or so until we have the correct game host there, and if they have been selected as the lobby host they are responsible for granting lobby ownership to the actual game host. When the game host has been assigned as Lobby host they will update the actual LobbyData with the correct joinCode and remove it from their playerData.
    • If a new player joins, they will first check all of the members playerData for a joinCode, if none is found they will use the joinCode from the lobbyData.
    I've gotten this system to work fairly well, and in most cases the host migration takes around 2seconds. But I find Unitys netcode solution to be pretty unstable, which also affects the reconnection success rate. So I've made it so that if for some reason the reconnection doesn't work the clients will just host their own game(new lobby + new relay). That way if all else fails they can at least keep playing and their friends can just join their new game.
     
    Last edited: Dec 5, 2022
    CodeNinja- and luke-unity like this.
  4. nico_st_29

    nico_st_29

    Joined:
    Mar 14, 2020
    Posts:
    68
    Thanks a lot for going through your solution. In the case of my game, given that everything pretty much gets run on both the clients and the host I've started working on adding the last few things remaining on the client.

    It would still be great if there was a way to change the host of an ongoing game in Unity Netcode for GOs.