com.unity.netcode.gameobjects icon indicating copy to clipboard operation
com.unity.netcode.gameobjects copied to clipboard

IsOwner is False on owning Client when spawning object during ConnectionApproval.

Open JayPeet opened this issue 3 weeks ago • 2 comments

Description

When spawning a Player object and a Player Owned object during Connection Approval, the Network Behaviors on the Clients GameObjects report IsOwner as being false, while the Network Object reports IsOwner as true.

It seems based on my repro project that the client receives the CreateObjectMessages for their Player and Owned object before their ConnectionApproved message. This causes the client to not have an assigned clientId so when the Network Behaviours initialze, they set IsOwner as false (Although, the Network Object seems to include IsOwner as part of its serialized data). My guess is that this is some kind of knock on from distributed authority being added to the library (I am 99% certain I have done what is happening in this repro here in other projects from a couple years ago...)

I think that the fix here (at least if I was to rework my code) would be to do all spawning once the connection is approved and once the client reports they are ready to get their objects (somehow)

I have included a small repro project with this report.

Reproduce Steps

Download the repro project. ownership_issue_repro.zip

  1. Go to Scenes and open Bootstrap
  2. Click the MPPM dropdown near the play button and select "Client - Direct Connection"
  3. Click Play, and wait for the client to connect to the host.
  4. Find their Player(clone) or ClientObject(clone) object in the hierarchy, note that IsOwner is True on the Network Object.
  5. Switch inspector to Debug mode, and look at the IsOwner field on the attached network behaviors. Note that it is set to false.
  6. Additionally, note that some random network objects are in the hierarchy, which were never spawned.

Actual Outcome

Objects spawned during ConnectionApproval are incorrectly set as IsOwner = false;

Expected Outcome

Either an error / warning is called when spawning with ownership for a none approved client OR the spawn messages are only sent once the approval is complete and the client has their clientId

Environment

  • OS: Windows 11
  • Unity Version: 6000.0.60f1
  • Netcode Version: 2.7.0
  • Netcode Topology: Client-Server

JayPeet avatar Nov 29 '25 00:11 JayPeet

Modifying the Approval async task to wait for the client to no longer be pending (So after the ConnectionApproval message has been sent) fixes this problem!

Eg before spawning any of the player objects:

while (networkManager.PendingClients.ContainsKey(request.ClientNetworkId))
{
    await UniTask.WaitForEndOfFrame();
}

As said before, I do think that it used to be fine to spawn objects for a player during the approval step (I remember there being some kind of message deferral where it would send them later? But its been a while since I have used this package). If this is no longer allowed, I think it might be possible to detect this when calling Spawn functions for a currently pending client.

JayPeet avatar Nov 29 '25 12:11 JayPeet

You've caught a definite problem here. We need to update both our docs and fix some code here.

The ConnectionApprovalCallback is called to decide whether or not the incoming client is allowed to connect. After the ConnectionApprovalCallback responds to the request the system then actually creates and connects the user. So by calling SpawnWithOwnership before the client has connected, you're assigning ownership to a client that doesn't exist yet. There's definitely a bug there that we don't check for that!

The ClientNetworkId that arrives in the ConnectionApprovalRequest is actually the transport id of that client (there's a more detailed answer on the difference between transport and client id here). The client id won't be calculated until after the incoming connection request has been fully approved.

To spawn a player prefab using the ConnectionApprovalCallback you'll need to set the following fields

        private void ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
        {
            response.Approved = true;
            response.CreatePlayerObject = true;
            response.PlayerPrefabHash = playerNetworkPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash
        }

This setup will spawn the given prefab as the player for that user. The spawn will happen after the user is fully connected and so IsOwner will be correctly set.

For further information, we have documentation for Spawning PlayerObjects and for the ConnectionApprovalCallback. Though those docs are absolutely missing a section on order-of-operations.

EmandM avatar Dec 01 '25 19:12 EmandM