Netcode for GameObjects: Client Spawning as Host + Client

Hey everyone,

I’m working on a multiplayer VR project using Unity 2021.3.31f1 and Netcode for GameObjects (version 1.6.0). I’m running into a bizarre issue where, when my client connects, it spawns as both the host and the client.

Here’s what’s happening:

  • The host starts up fine, spawns a player with NetworkObjectId: 1.
  • When a client connects, it appears to connect successfully and shows up in the hierarchy.
  • The client spawns with NetworkObjectId: 2, but logs show it’s acting as both host and client (IsHost: True, IsClient: True).
  • The host isn’t seeing the client, and the client isn’t seeing the host, despite both instances running.

What I’ve Tried:

  • Ensuring only one instance of NetworkManager per scene and per game instance (using DontDestroyOnLoad).
  • Checking logs for OnNetworkSpawn()—confirmed that clients are triggering this.
  • Adding checks to prevent starting as a host/client if already running (NetworkManager.Singleton.IsClient || IsServer).
  • Manually setting connection data for client to 127.0.0.1 and port 7777.
  • Tested with two Unity Editor instances (cloned project)—same results.

Questions:

  • Could this be an issue with how UnityTransport is handling connections or IP binding?
  • Is there something I’m missing in the NetworkManager or Player prefab setup?
  • Any advice on additional debugging steps or things to check?

Thanks in advance for any insights—this one’s got me stumped!


If you call StartHost, then IsServer, IsHost and IsClient will be true.
If you call StartClient, then only IsClient will be true.
I’m guessing you made some mistake here somewhere along the line. I can guess because you made conditionals like (IsHost && IsClient) where the former implies the latter, as well as the opposite (IsClient && !IsHost). Together with combining this in an if/else if block could easily fool you into a situation where one branch is always or never taken, or worse, taken in the wrong instance. I assume you made these because something feels off and then you decided to “make sure”. Eventually this leads to broken assumptions and broken code (I pointed out a few oddities below that you implemented during the process of combatting the observed effect). What you really need to do here is perform an analysis, what’s what, what’s the sequence of actions, what are the resulting states.

It is always bad practice to write conditionals that repeat the same condition again in an else if, like this:

if (someStatement && ...)
else if (!someStatement && ...)

This is best rewritten to something like this with an outer guard clause:

if (someStatement)
    if (...)
else
    if (...)

Or just not using an else if when it’s not appropriate.
I mention this specifically because time and time again I have seen network code where users happily go about doing all kinds of checks they don’t seem to fully understand, ie something like this:

if (IsHost && !IsOwner)
else if (!IsHost && IsClient && !IsOwner)
else if (IsServer && !IsClient && IsOwner)

That makes such conditionals insanely convoluted and hard to decipher. I’m pretty sure the above makes no sense whatsoever and probably one branch is never taken due to mutual exclusiveness. But I’m afraid that’s the kind of network code we see all too often.

Note that v1.6 is pretty old and you should upgrade, especially since you seem to just be starting with a project (assuming you can’t get a client to connect properly). The same goes for Unity 2021.3 which is no longer supported. With Unity 6 you can incredibly speed up your iteration workflow by using Multiplayer Playmode.

Also, please post code as text, not screenshots. We can’t quote this, it’s hard to read, not searchable, etc.

It may matter where you log this. In a network spawned object, only log in OnNetworkSpawn or thereafter.

Are you sure you’re not calling StartHost in both instances and yet happen to have connected both hosting instances as client with one another, respectively that they aren’t actually connected with one another respectively they are connected but just not with the same instance? You may have happened to have connected one instance with the editor, another with the build or the “wrong” editor instance, or something unexpected like that.

That is not upon you. NetworkManager already puts itself in DDOL without you doing anything. If you get NetworkManager duplication, put the NM object in the very first scene of the game and never load that “launch” scene again. This is the only way to solve it. It also guarantees the NM is fully initialized when other code is using it.

Again unnecessary. NGO will warn you of that case already. There’s also a flag IsListening which is true as soon as a session started regardless of role. You should use that to safeguard against repeatedly clicking one of the start buttons in case they don’t disable themselves instantly.

I just want to start out by first saying thank you so incredibly much for the thorough response and addressing everything that you did. As you can tell, I am very much a beginner and I have been attempting to make do, the best I can, with online guides and references I have found.

Also, yes I will post code as text from here on out. This is my first time posting on forums so thank you for you patience with me.

I believe I’m calling it correctly but I’m probably wrong. I will try to communicate more clearly and I’m sorry if I say things that are redundant.

I have a canvas with buttons to directly call Host and Client. Attached to that is the “NetworkManagerUI” Script below

public class NetworkManagerUI : MonoBehaviour
{
    [SerializeField] private Button serverBtn;
    [SerializeField] private Button hostBtn;
    [SerializeField] private Button clientBtn;

    private void Awake()
    {
        serverBtn.onClick.AddListener(() =>
        {
            NetworkManager.Singleton.StartServer();
        });

        hostBtn.onClick.AddListener(() =>
        {
            NetworkManager.Singleton.StartHost();
        });

        clientBtn.onClick.AddListener(() =>
        {
            NetworkManager.Singleton.StartClient();
        });
    }
}

I “build and run” a separate instance, hit play in the editor and click Host. Then on the other build I click “Client.”

2 Player prefabs show up in the hierarchy window.

The Log I get for the Host is “Player spawned. IsHost: True, IsClient: True, IsOwner: True, NetworkObjectId: 1”

The Log I get for when I click Client is: Player spawned. IsHost: True, IsClient: True, IsOwner: False, NetworkObjectId: 2

The code that gives me the console log is below. It is a component called “PlayerSpawnManager” on my PlayerPrefab. I wrote to change the location of the player when instantiated.

Do you think this is my problem? Maybe I shouldn’t have this on the player prefab?

public override void OnNetworkSpawn()
    {
        base.OnNetworkSpawn();

        Debug.Log($"[Netcode] Player spawned. IsHost: {IsHost}, IsClient: {IsClient}, IsOwner: {IsOwner}, NetworkObjectId: {NetworkObjectId}");
    }

Yes, you are 100% correct and most of the resources I find are working with more recent versions. I would love to swap over to a newer version but I’m working on a friends project that is pretty large and already published. It would be a last resort type of thing to migrate it to a newer version. Not impossible, just not ideal.

I will definitely be checking out your “Write Better Netcode.” THANK YOU

If you’re only looking at the host logs then the output is correct, the host is both the server and a client. The IsOwner flag specifically relates to the Player object and whether or not the local client/host has ownership. IsHost and IsClient pertain to the network manager.

Okay so then that wouldn’t be the reason for me being unable to see the host in the client’s view and the client in the host’s?

Do you mean in the game view, the players can’t see each other?

If this is a NetworkBehaviour on your player prefab, then you can just do the following check:

if (IsLocalPlayer)
{
  // Move your camera to the desired location.
}

However, you might think about looking at this example to get a general feel (and some additional helper components) for handling things like this.

In particular, the MoverScriptNoRigidbody.cs script provides a generic way of attaching the main camera to the local player and also provides you with a way to revert the changes back upon the player despawning.

Let me know if this helps any?

Yes, that is correct.

This is unbelievably helpful THANK YOU! I now have the host visible and moving around in the client’s view. I’m going to keep working on this but within just 30 minutes I already have progress. Seriously man, thanks for sharing this <3

1 Like