-
Notifications
You must be signed in to change notification settings - Fork 465
Description
Description
Ownership requests intermittently fail in Distributed Authority topology after restarting matches multiple times. When the bug occurs, RequestOwnership() returns RequestSent, but the callback OnOwnershipRequested is never invoked on the owner's side, and the requester receives CannotRequest in OnOwnershipRequestResponsed. This affects dynamically spawned NetworkObjects (bomb, gems) used for pickup mechanics. However, the session owner is always able to request ownership, this bug only occurs for other clients.
When the game ends, the players are put back to the lobby scene and all NetworkObjects are destroyed by having each client loop through all their owned NetworkObjects and calling networkObject.Despawn(false);. Note that I never call SetOwnershipStatus() anywhere. More code details below.
The issue is not consistently reproducible and typically manifests after completing several matches with different game modes. I'm not sure if this bug is a NGO issue or if it's specific to my project.
Reproduce Steps
- Set up a Distributed Authority session with multiple clients
- Spawn a NetworkObject (e.g., bomb) with
Ownershipset toDistributableandRequestRequired - Play through multiple matches, transitioning players between lobby and game scenes
- Despawn non-persistent owned objects when returning to lobby
- Start a new match and attempt to request ownership of a freshly spawned object
- At some point (non-deterministic), ownership requests stop working
Actual Outcome
RequestOwnership()returnsOwnershipRequestStatus.RequestSent- The owner never receives the
OnOwnershipRequestedcallback - The requester receives
OwnershipRequestResponseStatus.CannotRequestinOnOwnershipRequestResponsed - The ownership status flags on the NetworkObject appear correct on both sides
- Once bugged, repeated requests do not resolve the issue
- Some objects in the same session work correctly while others don't
Expected Outcome
- Owner should receive
OnOwnershipRequestedcallback - If approved, requester should receive
OwnershipRequestResponseStatus.Approved - Ownership transfer should complete successfully
Screenshots
A screenshot of the NetworkObject of the bomb (object to pick up) on the Client trying to pick up and SessionOwner. Please note that they are taken in different matches, therefore the difference in ID.


That's how I configured the NetworkObject in the editor.

Environment
- OS: Windows 11
- Unity Version: [Please specify]
- Netcode Version: 2.7.0 (also reproduced on 2.5.1)
- Netcode Commit: [If known]
- Netcode Topology: Distributed Authority
Additional Context
Ownership request code (on player):
if (bombNetworkObject.IsOwner)
{
RequestBombPickUp(bombNetworkObject);
}
else
{
RequestBombOwnership(bombNetworkObject); // Client tries requesting ownership
}
private void RequestBombOwnership(NetworkObject targetObject)
{
if (!targetObject.IsRequestInProgress)
{
targetObject.RequestOwnership(); // returns OwnershipRequestStatus.RequestSent
}
}Ownership callbacks (on bomb):
private bool EventOwnershipRequested(ulong clientRequesting)
// Owner doesn't receive it when it bugs, otherwise does
{
// return false in some specific scenarios
return true;
}
private void EventOwnershipRequestResponse(NetworkObject.OwnershipRequestResponseStatus ownershipRequestResponse)
// CannotRequest when it bugs, otherwise Approved
{
if (ownershipRequestResponse == NetworkObject.OwnershipRequestResponseStatus.Approved)
{
// Pick up
}
}Scene transition cleanup:
public static void DespawnNonPersistentOwnedNetworkObjects()
{
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsClient)
{
Debug.LogWarning("NetworkManager is not initialized or this is not a client instance.");
return;
}
ulong localClientId = NetworkManager.Singleton.LocalClientId;
NetworkObject[] ownedObjects = NetworkManager.Singleton.SpawnManager.GetClientOwnedObjects(localClientId);
foreach (NetworkObject networkObject in ownedObjects)
{
if (networkObject != null && networkObject.IsSpawned)
{
if (networkObject.gameObject.scene.name != "DontDestroyOnLoad")
{
networkObject.Despawn(false);
}
}
}
}Observations:
- Using the latest NGO 2.7.0 package
- The bug appears related to match restarts and scene transitions
- Multiple object types are affected (bomb, gems)
- NetworkObject settings:
Ownership = Distributable, withRequestRequiredenabled - Possibly related to internal state not being cleaned up correctly during session/scene transitions