Mirror icon indicating copy to clipboard operation
Mirror copied to clipboard

duplicate Awake() causing destroy - move fix for issues/2778 to second pass

Open Destraag opened this issue 4 years ago • 2 comments

Unity 2020.3.22f1

Please move the fix from issue 2778 to the second pass NetworkServer.SpawnObjects() -https://github.com/vis2k/Mirror/blob/b2ce3421e50dc6082d207febc703a60c7798d976/Assets/Mirror/Runtime/NetworkServer.cs#L1107-L1115

Upon upgrading mirror in my project to the latest version in the asset store I found that several of my objects were being destroyed and throwing an error as NetworkIdentity.Awake() was being called twice. I believe this is because on the first loop the parent object with network identity is still inactive when the child with the network identity is checked for !identity.gameObject.activeInHierarchy.

https://github.com/vis2k/Mirror/blob/b2ce3421e50dc6082d207febc703a60c7798d976/Assets/Mirror/Runtime/NetworkIdentity.cs#L325-L330

By moving this check to the second loop we ensure that all game objects will be set to active before we check to see if we need to manually call Awake().

For my project I have applied:

       foreach (NetworkIdentity identity in identities)
        {
            if (ValidateSceneObject(identity))
            {
                // Debug.Log($"SpawnObjects sceneId:{identity.sceneId:X} name:{identity.gameObject.name}");
                identity.gameObject.SetActive(true);
            }
        }

        // second pass: initialize nested scene object if parent is inactive and spawn all scene objects
        foreach (NetworkIdentity identity in identities)
        {
            // fix https://github.com/vis2k/Mirror/issues/2778:
            // -> SetActive(true) does NOT call Awake() if the parent
            //    is inactive
            // -> we need Awake() to initialize NetworkBehaviours[] etc.
            //    because our second pass below spawns and works with it
            // => detect this situation and manually call Awake for
            //    proper initialization
            if (ValidateSceneObject(identity))
            {
                if (!identity.gameObject.activeInHierarchy) {
                    //identity.Awake();  - See related issue below
                    identity.InitializeNetworkBehaviours();
                }
                Spawn(identity.gameObject);
            }
        }

More directly on the duplicate Awake() calls but slightly different - issues/2778 may need further review though its gone/archived and I do not see the original details at the link.

If the inactive object(s) that has already had its NetworkIdentity.Awake() called from this fix is then activated... it will also throw the error and be destroyed on this second Awake() call as hasSpawned will be true. - https://github.com/vis2k/Mirror/blob/b2ce3421e50dc6082d207febc703a60c7798d976/Assets/Mirror/Runtime/NetworkIdentity.cs#L325-L331

So instead of if (!identity.gameObject.activeInHierarchy) { identity.Awake(); }

possibly if (!identity.gameObject.activeInHierarchy) { identity.InitializeNetworkBehaviours(); }

Or similar method that calls InitializeNetworkBehaviours() and more if additional logic that must be separate from InitializeNetworkBehaviours() is required. Possibly also a different bool to ensure InitializeNetworkBehaviours() or the new method is bypassed on the potential future Awake() call when the object is activated.

This seems to still leave a gap on the client side for these same objects that are not initialized and another bit of logic may need to be added. Potentially to this loop - https://github.com/vis2k/Mirror/blob/b2ce3421e50dc6082d207febc703a60c7798d976/Assets/Mirror/Runtime/NetworkClient.cs#L1129-L1136

something similar to the final form of the above logic for the server or a dry shared method if that makes sense here...

        foreach (NetworkIdentity identity in allIdentities)
        {
            // add all unspawned NetworkIdentities to spawnable objects
            if (ConsiderForSpawning(identity))
            {
                spawnableObjects.Add(identity.sceneId, identity);
            }
            // initialize nested scene object if parent is inactive
            if (NetworkServer.ValidateSceneObject(identity))
            {
                if (!identity.gameObject.activeInHierarchy) {
                    identity.InitializeNetworkBehaviours();
                }
            }
        }

I applied these changes and have my project working again in the short term.

Thanks

Destraag avatar Nov 12 '21 13:11 Destraag

thanks for details, gonna take a look

miwarnec avatar Apr 04 '22 04:04 miwarnec

for reference. original issue: https://github.com/vis2k/Mirror/issues/2778 original fix: https://github.com/vis2k/Mirror/commit/2c2581fa08cc9f9611ac3ab8fbca73b3275e9e33

miwarnec avatar Apr 04 '22 04:04 miwarnec

I know this post is old, but it just saved me today. Thank you.

ItsCodeRed avatar Dec 04 '22 01:12 ItsCodeRed

will fix this soon, sorry for delay.

miwarnec avatar Jan 05 '23 21:01 miwarnec

fixed. https://github.com/MirrorNetworking/Mirror/pull/3347

miwarnec avatar Jan 06 '23 21:01 miwarnec