Skip to content

Calling StartClient() from within an OnServerStopped() callback leads to inconsistent state #2703

Open
@xaroth8088

Description

@xaroth8088

Description

Calling StartClient() or StartHost() from within an OnServerStopped()/OnClientStopped() callback leads to inconsistent state.

Reproduce Steps

  1. NetworkManager.Singleton.OnServerStopped += MyOnServerStoppedHandler
  2. NetworkManager.Singleton.StartHost()
  3. Wait for a few seconds
  4. NetworkManager.Singleton.Shutdown(true)
  5. Inside MyOnServerStoppedHandler, call NetworkManager.Singleton.StartClient()

Actual Outcome

A few tidbits of client state are cleared out at the tail end of NetworkManager::InternalShutdown() that were just set up inside of StartClient()/StartHost(). For example, StartClient() calls ConnectionManager.LocalClient.SetRole(false, true, this); as part of that callback, but then a moment later InternalShutdown() calls ConnectionManager.LocalClient.SetRole(false, false);.

Expected Outcome

Any of these three options would be fine:

  1. (preferred) All shutdown work is complete and settled before OnServerStopped()/OnClientStopped() are called, permitting StartClient()/StartHost() to be called from within those callbacks.
  2. A new OnNetworkManagerShutdownComplete callback happens when shutdown is well and truly complete.
  3. StartClient()/StartHost() detect that shutdown isn't quite finished yet, and bail by returning false (preferably with some sort of error log explaining why).

Environment

  • OS: N/A
  • Unity Version: N/A
  • Netcode Version: 1.6.0

Additional Context

I bumped into this as a problem while trying to accommodate a flow of "I used to be hosting, but then I got an invite from a friend and want to switch to being a client that's connected to their host". To work around the problem, I had MyOnServerStopped() call Invoke(nameof(OnServerFullyStopped), 0.01f), and then called StartClient() from within OnServerFullyStopped().

This workaround caused an issue for me in a different way, though, because SteamNetworkingSocketsTransport does a similar workaround during their shutdown. Critically, they wait for 0.1f seconds before finishing the transport's cleanup. This resulted in a race condition where my game would be halfway through connecting to its new host when the transport would wrap up shutting itself down.

For the time being, I can work around that problem by simply waiting longer for everything to settle before calling StartClient(), but clearly that's suboptimal all around.

All of that said, if I'm simply missing something in the docs on how my code can know that NetworkManager is really, truly safe to call StartClient()/StartHost(), or if I'm going about it wrong and should instead destroy/recreate my NetworkManager for this particular flow, please let me know!

Metadata

Metadata

Assignees

No one assigned

    Labels

    TrackingHas been added to trackingstat:importedStatus - Issue is tracked internally at Unitytype:docsDocs feedback or issuetype:featureNew feature, request or improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions