Seamless Host Reconnection (Not Host Migration) in Netcode for GameObjects

Hi, I’m using Netcode for GameObjects, and my application can run either in host mode or client mode. In the host scenario, the application creates a relay and publishes a join code, which allows other instances (clients) to connect essentially as spectators. The join code itself is distributed outside of Netcode via a completely separate channel.

What I’m trying to achieve is graceful handling of host disconnection. If the host loses its network connection, I’d like the application to continue running as if nothing happened and automatically attempt to reconnect (without having to call a full network shutdown).

Creating a new relay with a new join code is not an issue. However, I need the transition to be seamless from the host user’s perspective (they shouldn’t notice the disconnect/reconnect cycle). Ideally, once the host reconnects, clients should be able to join again (potentially using a new join code).

Is there a recommended approach or best practice for handling this kind of reconnection scenario? I am not sure how to how to achieve this.

Thanks! :slight_smile:

Pretty much Distributed Authority. This is a paid service but has a free tier.

I think Netcode for Entities has host migration as experimental feature, but that’s a whole different beast.

If you need to do it yourself … well, the first solution is: don’t do it. Why? It’s complicated.

First, a seamless transition is perhaps not even possible or at least quite a challenge to implement yourself. Connected clients will definitely always experience a hiccup which may be anywhere between 1-5 seconds no matter the technology. You would likely need to display a “Reconnecting …” message to clients to prevent them from quitting.

For NGO you will need to do a soft shutdown and reconnect. That means serializing the current state all the time, meaning: keep all network state separate from actual netcode’s state. If you use the Lobby service, at least you have a place where you can keep all things together, both clients and game state. Then restart a session.

The problem is, once the host got disconnected the session has already shutdown, which means objects have despawned/destroyed. There is nothing you can do to detect ahead of time that the host is going to lose connection. Once the host stops sending packets, the game stops for everyone else and that’s that. Netcode Shutdown fires automatically after the timeout.

Besides that … which of the connected clients would you pick as the host? You can’t randomly pick one, because that might be a user with a high ping or high packet loss. So you’d have to assess the quality of each client’s connection either during host migration (adding to the hiccup time) or constantly monitor it.

I guess you mean “the new host user’s perspective” and “once the new host connects” since the previous host has dropped the connection nor is the previous host coming back anytime soon when they disconnected due to an actual computer crash or connectivity issue.

If not, perhaps you mean something else entirely by host reconnection, since you mentioned “not host migration”. That I don’t quite understand. From networking topolofy, there is no such thing as a “client mode” but only client-hosted topology (where you’d have a host acting as the server) and dedicated server topology (where you wouldn’t have a host).

Hi, thank you very much for the detailed reply. I didn’t express it very clearly, but the host is always a single specific device, while the clients may vary. Therefore, it’s not possible to use distributed authority. The clients really behave only as spectators, and one specific device (the VR headset) always represents the host.

The idea was that if the host (the VR headset) disconnects but is able to reconnect afterward, the VR user wouldn’t experience any interruption from the disconnect/reconnect cycle. At the same time, the clients (spectators) should be able to reconnect as well, even if it requires using a new join code.

Hi @NadaZilch,
I think you want to use a combination of Lobby DataObjects, NetworkVariables, and storing off any state you want to restore in a class (or the like) that is not destroyed upon a session ending in order to handle this scenario.
Here are the things (with some high-level steps) you can do to achieve a reconnection flow for a session that was terminated by the host (lost connection due to service issue, mistake, or otherwise).

Establish a reconnect key
If you are wanting the same host and same client to reconnect together, then you will need to have a way to establish a link between them. One way is to have the host send out a unique key to clients upon connecting.

  • Various ways to generate a unique identifier (could be based on user-name + GUID or some other unique identifier).
  • This could be synchronized by a NetworkVariable on some management system (i.e. in-scene placed or migrated into DDOL).
  • Upon connecting, have clients that connect to that session store off the key on something that does not get destroyed upon leaving a session (i.e. clients and the host will want to use this if the host disconnects).

Upon Host Disconnecting
If the host disconnects, as @CodeSmile pointed out, then everyone disconnects (i.e. NetworkManager for everyone will automatically shutdown).
Upon this happening:

  • The host can re-launch a new lobby with public meta data (DataObject) that the clients will use as part of their search criteria.
  • Since the clients that were part of the original session already obtained the “rejoin key” generated by the host, clients would search for a lobby with that specific key..
  • Upon finding the lobby… they use the same process they did originally to get the relay join code.
    • They establish the relay connection and then the NetworkManager is started (Client) and the client(s) reconnect.

Persisting Data
While NGO does not have a host migration, @CodeSmile mentioned distributed authority mode as the alternative, you can store off specific states you want to restore on the host side. Upon reconnecting, the host can provide any client specific information.
You might also think about adding a “unique client identifier” into the mix when a client first joins:

  • Client joins a session:
    • The client stores off the host “re-join key” in the event the host is disconnected.
    • There are two ways you can do this:
      • Client generates the unique client identifier key:
        • The client’s player has an owner write NetworkVariable that has server read permissions.
        • The client sets a unique identifier for itself on its player prefab instance.
          • The host subscribes to changes in this and upon the client setting a value the host associates persisted state/data for the client in a table (stored on something not destroyed if the session ends).
      • Host generates the unique client identifier key:
        • The client’s player has a server write NetworkVariable that has owner read permissions.
        • The host provides a unique identifier it generates and applies to the NetworkVariable upon spawning the player.
        • This can be done in the OnNetworkSpawn or OnNetworkPostSpawn methods within an IsServer logical if gated block of script where upon spawning locally on the host side the host applies the unique identifier before the CreateObjectMessage for the Client’s player is sent.
          (Which of the two ways you pick is purely your choice. Both ways are relatively secure and only the host and the client will know about the assigned client identifier.)
    • Upon a re-join, the client sends an RPC to the server with its identifier and the host provides any client-side specific state the client should apply.
      • The host can apply most states prior to any client re-joining. The only states you would need to send back to the client would be any client-owner specific states (i.e. typically NetworkVariables that are owner write. If you are using a client authoritative motion model, the host can re-spawn the client’s player in the host’s last known client transform state.

State saving and frequency
The final thing (if you are restoring state) to think about is how you want to save off states and how often you save them (i.e. depending upon what you are saving depends upon how much processing time it takes to save off this information).

  • Saving states:
    • You can create a “global” (possibly static) class that holds any states you want to preserve.
    • The host is the only one saving states (source of “session truth”).
    • Look at the classes you are saving. There very likely is some form of notification/event or virtual method you can use to be notified of changes to state.
      • NetworkVariable has the OnValueChanged event that you can leverage from.
      • NetworkTransform has a virtual method OnNetworkTransformStateUpdated that you can use.
        • You would:
          • Create a custom version of NetworkTransform (i.e. derive from it).
          • Override OnNetworkTransformStateUpdated which is invoked each time the host is updated by a client/owner authoritative motion model NetworkTransform (i.e. player’s position and rotation updates).
            • You don’t want to save every state update (that would be too much information), but you could use a preconfigured update frequency (i.e. 1 or 2 times per second) that simply saves the entire player’s transform so when a client reconnects it is “within half a second” accurate.
  • State saving frequency:
    • For things that change frequently, use the “update frequency” approach.
    • For things that change periodically (i.e. NetworkVariable that denotes a tree is chopped down or a door is unlocked) you would save this when the state changes.

Let me know if this path provides you with a solution to your project’s needs/goals?