Skip to content

NetworkShow() follow by Despawn livelocks the server #3023

@SamTheBay

Description

@SamTheBay

Description

If you call NetworkShow() on a NetworkObject and then Despawn(true) before the network tick runs it gets the server into a livelock state that requires it to be restarted. This is because the NetworkObject is added to a queue in the SpawnManager which will be executed on the next network tick. However, the NetworkObject is destroyed by the time the next network tick occurs. When the SpawnManager tries to show the NetworkObject it attempts to reads the transforms parent, which throws and exception since the NetworkObject has been destroyed already. The SpawnManager will repeat attempting to show the destroyed NetworkObject on every network tick from then on since it is never removed from the queue, thus livelocking the server.

Reproduce Steps

Code like this will reproduce it...

const int c_spreadFrames = 300;
if (testNetItem != null)
{
    if (testStage == 0)
    {
        testNetItem.NetworkObject.NetworkHide(testPlayer.OwnerClientId);
        testStage = 1;
    }
    else if (testStage < c_spreadFrames)
    {
        testStage++;
    }
    else if (testStage == c_spreadFrames)
    {
        testNetItem.NetworkShow(testPlayer.OwnerClientId);
        testNetItem.NetworkObject.Despawn(true);
        testStage = c_spreadFrames + 1;
    }
    else
    {
        testNetItem = null;
        testStage = 0;
    }
}

Note: You must spread out the NetworkHide from the NetworkShow by at least one network tick. Otherwise, it will not repro because there is logic to protect against trying to hide and show in the same frame.

Actual Outcome

Server gets into a stuck state in which no new objects are able to spawn for clients.

Expected Outcome

Server should discard the destroyed item from its spawn queue since it is no longer a valid action.

Environment

  • OS: [e.g. macOS Monterey]
  • Unity Version: 2022.3.34f1
  • Netcode Version: 1.8.1 (verified 1.9.1 has the same issue)

Additional Context

I seem to be able to work around the issue by creating my own class SafeNetworkBehaviour and keeping track of the last time a NetworkShow() call was made. Then, in the OnNetworkDespawn() method I check if NetworkShow was called recently. If it was, I go through and call NetworkHide on the recently shown clientIds. This will remove it from the SpawnManager queues before the NetworkObject is destroyed. However, this seems super hacky and it would be better if the networking code handled this by default.

Metadata

Metadata

Labels

priority:highThis issue has high priority and we are focusing to resolve itstat:importedStatus - Issue is tracked internally at Unitytype:bugBug Report

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions