Unity-Technologies / com.unity.netcode.gameobjects

Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.
MIT License
2.14k stars 434 forks source link

Build error when compiling using IL2CPP for Android if project uses static NetworkVariables #3101

Open lllando opened 3 days ago

lllando commented 3 days ago

Description

Build error when compiling using IL2CPP for Android if project uses static NetworkVariables. This behaviour happens when attempting to build using IL2CPP scripting backend and building to Android. Building to Android using Mono builds successfully with no errors, leading me to believe this is a bug/unintended.

Reproduce Steps

Actual Outcome

Build fails with two error messages in console.

Expected Outcome

Build should compile successfully with no errors.

Screenshots

Error in Console: image

Environment

Additional Context

Full Error Logs:

Building Library\Bee\artifacts\Android\iz17e\25857ix1c827.o failed with output:
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5692,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_0 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5708,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_3 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5711,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_4 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5714,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_6 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5717,78): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_7 = __this->___OtherClientPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5733,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_10 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5736,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_11 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5739,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_13 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
8 errors generated.

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
Building Library\Bee\artifacts\Android\d8kzr\25857ix1c827.o failed with output:
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5692,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_0 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5708,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_3 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5711,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_4 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5714,78): error: no member named '___HostPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_6 = __this->___HostPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5717,78): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_7 = __this->___OtherClientPlayerHealth;
                                                                                   ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5733,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_10 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5736,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_11 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
L:\Career\Development\Unity\GitHub\PrototypeCRF\Library\Bee\artifacts\Android\il2cppOutput\cpp\Assembly-CSharp.cpp(5739,79): error: no member named '___OtherClientPlayerHealth' in 'HubManager_t257E0CCDF9E261EA4899F86EEF4B1C6AAEEF8922'
                NetworkVariable_1_t72006E2365F7131BBF61213343C271666361F504* L_13 = __this->___OtherClientPlayerHealth;
                                                                                    ~~~~~~  ^
8 errors generated.

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
NoelStephensUnity commented 1 day ago

@lllando You cannot use static NetworkVariables on any platform as each NetworkVariable needs to know what NetworkObject and NetworkBehaviour instance the property belongs to in order to properly be synchronized between clients.

If you want to use NetworkVariables to store a global state, then I would recommend a separate in-scene placed NetworkObject.

lllando commented 9 hours ago

@NoelStephensUnity Ok good to know, thanks - like you said it is easy to work around. Interesting that I only ran into the error when switching away from Mono though.

Could you shed some more light as to why a NetworkVariable would need to know which instance the property belongs to even in the case of static variables? My understanding is that by definition any instance could be used as the information held by the static NetworkVariable would be the same across all instances.

NoelStephensUnity commented 1 minute ago

@lllando I would be happy to shed some light on why static NetworkVariables shouldn't be used and why they should cause issues if you try to use them. In order to fully understand this I have to sort of walk through how GameObjects are identified and synchronized.

A NetworkObject component added to a GameObject provides the ability for the object instance to be replicated across clients. This is accomplished by spawning the NetworkObject. When a NetworkObjet is spawned, it is assigned a NetworkObjectId and is owned by one of the clients or the host/server.

With the client-server network topology, the host/server controls the spawning, ownership, and despawning of the NetworkObject. This means the network prefab (at minimum a GameObject with a NetworkObject component) is instantiated first on the server/host side and then spawned. Once spawned, a CreateObjectMessage is sent to all of the clients who then create a clone instance and spawn that clone instance locally where the serialized properties of the NetworkObject and any NetworkBehaviour components are applied.

With the distributed authority network topology, the owner controls the spawning, and despawning of the NetworkObject. Control over ownership is defined by the ownership flags. This means the network prefab is instantiated first on the owning client side and then spawned. Once spawned, a CreateObjectMessage is sent to all of the clients who then create a clone instance and spawn that clone instance locally where the serialized properties of the NetworkObject and any NetworkBehaviour components are applied.

NetworkBehaviour components are associated with a specific NetworkObject instance and each NetworkBehaviour component is assigned a NetworkBehaviourId. When a message is sent between instances, the NetworkObjectId and NetworkBehaviourId are used to route the message to the correct NetworkObject and NetworkBehaviour instances.

NetworkVariable properties are defined at a NetworkBehaviour scope. This means that if a NetworkVariable property changes then to deliver the NetworkVariableDeltaMessage to other clients it uses the NetworkObjectId, NetworkBehaviourId, and and internally tracked NetworkVariable identifier.

NetworkObjectId-->NetworkBehaviourId-->NetworkVariable Identifier--> apply delta state

So, if you have a NetworkVariable defined in a NetworkBehaviour that is say...on a player... then when that NetworkVariable is updated it updates on all instances of that specific player. Since it is common to have the same network player prefab used to create player instances for all clients connected to a session, each client would have a unique instance cloned/replicated on each client (and server or host if using client-server). This means that any changes to a NetworkVariable of a client's player instance will be replicated across all clients (and server or host if using client-server) to reflect the change in the value of the NetworkVariable.

Since NetworkVariable properties have read/write permission settings and those settings can be specific to an owner, if you have a NetworkVaraible that is set to owner write permissions then if that NetworkVariable property was static player (using the scenario before) could make changes to the NetworkVariable but which instance would be updated and what happens if multiple clients change the static NetworkVariable at the same time (i.e. a race condition would occur). Then there is the issue of registering with events...using the player prefab scenario if you wanted a client to receive notifications when a static NetworkVariable changed and say you subscribed within the OnNetworkSpawn method. then you would end up registering multiple times for the same NetworkVariable property instance (since it is static) and would receive multiple event notifications for a single change in the value (roughly one for each client).

However, you do bring up a good point and we should have some form of notification or block during compilation that provides you with a message about this and it does appear that it will allow you to use it (which it shouldn't) under other platforms (Windows, OSX, etc.).... which it shouldn't do that.

I will track this issue with our QA (@fluong6 ) and see if we can get some better documentation first and then possibly detect this use case and just not allow it.