OnNetworkSpwan Instantiation order

I have a player GameObject that has multiple scripts attached to it that control different things related to the player such as health, points, etc
At the same time, I have a second GameObject that is a leaderboard, that receives events from the player.

My problem is that I have 2 events that relate to the leaderboard, the player name, and the player spawn that at the moment are been called in the inverse order.
I try to change the order on the project settings Script Execution Order, however, it seems that the OnNetworkSpawn is not respecting this order.

Any suggestion in how I can get the OnNetworkSpawn called in the correct order?

Regarding the order in which NetworkBehaviour.OnNetworkSpawn is invoked:
If the NetworkBehaviour components are on the same GameObject, then the order of the components (as viewed in the inspector view) will be the order in which OnNetworkSpawn is invoked.
For a complex nested child hierarchy where you have a NetworkObject at the root and then several nested GameObjects at the same or lower generation layers, pretty much the same “rule of thumb” applies where the order of the child GameObject relative to other children denotes the order of that GameObject’s NetworkBehaviour components will be invoked relative to the parent and the other same generation children.
To see how NetworkBehaviours are ordered you can use this:
GetComponentsInChildren<NetworkBehaviour>(true);
Which will provide you with the same ordered list that NetworkObject uses as it is invoking OnNetworkSpawn.
Here is a small script you can include in your project that will allow you to right click on a prefab asset and log the order of any NetworkBehaviours on it:

#if UNITY_EDITOR
    public static class NetworkBehaviourHelper
    {
       
        [UnityEditor.MenuItem("Assets/NetworkBehaviour Order")]
        public static void DisplayNetworkBehaviourOrder()
        {
            var gameObject = UnityEditor.Selection.activeObject as GameObject;
            if (gameObject == null)
            {
                return;
            }
            var networkObject = gameObject.GetComponent<NetworkObject>();
            if (networkObject == null)
            {
                return;
            }
            var networkBehaviourOrder = new System.Text.StringBuilder();
            var networkBehaviours = gameObject.GetComponentsInChildren<NetworkBehaviour>(true);
            networkBehaviourOrder.AppendLine($"{gameObject.name}'s NetworkBehaviour Order:");
            var count = 0;
            foreach (var networkBehaviour in networkBehaviours)
            {
                networkBehaviourOrder.AppendLine($"[{count}][{networkBehaviour.gameObject.name}][{networkBehaviour.GetType().Name}]");
                count++;
            }
            Debug.Log(networkBehaviourOrder);
        }
    }
#endif

When you right click on an asset you will see the added menu item:

In the console log you will see something like:


Which provides you with the order in which OnNetworkSpawn will be invoked on those NetworkBehaviour components.

Regarding waiting for other assets to be instantiated:
If you run into order of operations issues due to one network prefab being spawned before another when first synchronizing a client, then the best option would be to have your scripts subscribe to NetworkManager.OnClientConnectedCallback (in the Awake method using NetworkManager.Singleton). This is invoked when all NetworkObjects have been instantiated and spawned so you know that whatever other network prefab instance dependency you are looking for will/should exist.

For something like a leader board where you could have multiple dependencies, you can also think about making a “registration class” that is created and contains the properties required (i.e. pointing to other NetworkObject/GameObject instances) to consider the “player leader board registration” complete. When you first add it to say a Dictionary key’d by the cleint identifier, when updating the leaderboard you can parse through the registrations and have a method that just validates whether “everything has registered” prior to trying to display the entry (where if everything has registered with the entry it will display it with all details).

Hey I’m guessing maybe you found a solution to this? I’m facing the same issue currently but while I know what a solution might look like, I’m curious if you found a more official way of dealing with it?

I was thinking to use System.Reactive.Subjects as such:
Player.cs spawns its PlayerModel.cs
PlayerModel.cs subscribes to a subject BY clientId on GameEngine.cs
e.g
GameEngine.Instance.PlayerNetworkSpawnSubjectsByClientId<ulong,Subject<Player>>

When Player.cs runs OnNetworkSpawn() I call:
GameEngine.Instance.PlayerNetworkSpawnSubjectsByClientId[OwnerClientId].OnNext(this) from GameEngine.cs’s Subject instance

In the case where Player spawns first, it will call OnNext() and the Subject would have a reference to the spawned Player instance, so the next subscription would be fed this value instantaneously.

In the case where PlayerModel spawns first, it would subscribe to the subject and wouldn’t continue until the Player spawns and calls OnNext()