-
Notifications
You must be signed in to change notification settings - Fork 464
Description
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.