Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
lllando opened this issue Oct 15, 2024 · 5 comments
Labels
priority:medium stat:import Status - Issue is going to be saved internally

Comments

@lllando
Copy link

lllando commented Oct 15, 2024

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

  • Declare a static NetworkVariable in a NetworkBehaviour script. (public static NetworkVariable<int> in my case)
  • Set scripting backend to IL2CPP
  • Set build platform to Android
  • Build
  • Error displayed as shown in Additional Context section

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

  • OS: [Windows 10]
  • Unity Version: [2022.3.9f1]
  • Netcode Version: [1.5.2]
  • Netcode Commit: [e.g. https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/commit/ba418fa5b600ad9eb61fab0575f12fbecc2c6520]

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&)
@lllando lllando added stat:awaiting triage Status - Awaiting triage from the Netcode team. type:bug Bug Report labels Oct 15, 2024
@lllando lllando changed the title Build error when compiling to IL2CPP for Android if project uses static NetworkVariables Build error when compiling using IL2CPP for Android if project uses static NetworkVariables Oct 15, 2024
@NoelStephensUnity
Copy link
Collaborator

@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.

@NoelStephensUnity NoelStephensUnity added type:support Questions or other support stat:awaiting response Status - Awaiting response from author. and removed type:bug Bug Report labels Oct 18, 2024
@lllando
Copy link
Author

lllando commented Oct 19, 2024

@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
Copy link
Collaborator

@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.

@lllando
Copy link
Author

lllando commented Oct 20, 2024

@NoelStephensUnity That makes sense and I appreciate the detailed breakdown of how things are handled under the hood.

One thing to note is that it does currently let you use it on Android too as I had a build running on a different device (using Mono) although you would probably run into issues when attempting to publish it on Google Play. The error was only displayed when switching the scripting backend to IL2CPP to target 64 bit architectures.

Either way, I think the solutions you listed at the end would be welcome additions.

Thanks again for your help,

Lewis

@NoelStephensUnity NoelStephensUnity added priority:medium stat:import Status - Issue is going to be saved internally and removed type:support Questions or other support stat:awaiting response Status - Awaiting response from author. stat:awaiting triage Status - Awaiting triage from the Netcode team. labels Oct 24, 2024
@NoelStephensUnity
Copy link
Collaborator

Notes on this issue:
The related bug is a lack of prevention and/or a logged warning about using any form of static NetworkVariable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority:medium stat:import Status - Issue is going to be saved internally
Projects
None yet
Development

No branches or pull requests

2 participants