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.1k stars 430 forks source link

NullReferenceException: Object reference not set to an instance of an object Unity.Netcode.FixedStringSerializer`1[T].WriteDelta #2920

Closed aidanhorton closed 3 days ago

aidanhorton commented 1 month ago

Description

It's hard to be precise with this issue as I'm unsure exactly what's causing it. But in one of my scenes, I get the following error: Screenshot 2024-05-07 114130

It continuously spams the log while the game is playing. I've attempted to track down which object it is related to, and I've managed to delete an object, and even a script, which then stops the issue, but then it seems to come back again after a restart related to a different object. I couldn't track down any code that could've caused this - and with the stacktrace being entirely inside of Netcode, it's hard to figure it out.

The weird part is, as far as I remember, I had netcode 1.9.1 working in this exact scene with no errors for a while. I keep a full git history, so I went back to a point where it was working previously, and it is no longer working. I've deleted the Library, obj and other folders and still no luck.

There are cases where I've been trying to narrow down the object thats creating this bug - and I play the game with that object deleted, and still get the bug. But then I play the game again, and then the bug isn't there - and I haven't changed anything since the last play. If I leave said object deleted, restart Unity, then the first play will have this error, subsequent ones will not...

This scene works completely fine in NGO 1.7.1 & 1.8.1

Environment

Windows 11 NGO 1.9.1 Unity 2023.2.9f1

viper110110 commented 1 month ago

I'm also encountering this issue. It specifically occurs when Unity tries to serialize a NetworkVariable<FixedString32Bytes>. I am able to set the value of the network variable and read it again (changes appear locally) but they do not get sent over the network. The exception occurs every frame after the change.

My code works fine on NGO 1.8.1, but not on 1.9.1 Tested both NGO versions on Unity 2022.3.26 and Unity 2022.3.27 Windows 10

name change error

LightPat commented 1 month ago

I have also encountered this issue, on one of my network variables NetworkVariable<FixedString512Bytes> Exception occurs every frame after assigning a value to the variable Unity 2021.3.38f1. My code works fine on NGO 1.8.1 and 1.7.1. Windows 10.

romain-capot commented 1 month ago

I have the same error when updating to NGO 1.9.1.

The error occur the second time I update my variable NetworkVariable<FixedString64Bytes>

Environment

Windows 11 NGO 1.9.1 Unity 2022.3.29f1

arcv commented 1 month ago

Same issue in Netcode for GO 1.9.1. Couldn't find the root cause, could be transport class maybe?

"com.unity.netcode.gameobjects": "1.9.1",
"com.unity.services.relay": "1.0.5",
"com.unity.transport": "2.2.1",
Laumania commented 1 month ago

I wanna tip in on this, as I get the same - it happens when upgrading to NGO 1.9.1. In 1.8.1 this works fine.

For me it happens when I set a FixedString64Bytes. As said, the exact same code works fine in 1.8.1.

As other mentions, it's like it's when it's set after Spawned, so like second time, and then it just keep spamming with this error and the NetworkVariable is never set. The error happens only happens on the client (can also be the host) that sets the NetworkVariable. So if the Host sets this, the error happens only there not on Client. If Client sets it, it happens only on the Client not on the Host.

Which kind of make sense as the error indicate something is wrong in the "write/sending" part.

NullReferenceException: Object reference not set to an instance of an object Unity.Netcode.FixedStringSerializer1[T].WriteDelta (Unity.Netcode.FastBufferWriter writer, T& value, T& previousValue) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/NetworkVariable/NetworkVariableSerialization.cs:701) Unity.Netcode.NetworkVariableSerialization`1[T].WriteDelta (Unity.Netcode.FastBufferWriter writer, T& value, T& previousValue) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/NetworkVariable/NetworkVariableSerialization.cs:1642) Unity.Netcode.NetworkVariable1[T].WriteDelta (Unity.Netcode.FastBufferWriter writer) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/NetworkVariable/NetworkVariable.cs:198) Unity.Netcode.NetworkVariableDeltaMessage.Serialize (Unity.Netcode.FastBufferWriter writer, System.Int32 targetVersion) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs:111) Unity.Netcode.NetworkMessageManager.SendMessage[TMessageType,TClientIdListType] (TMessageType& message, Unity.Netcode.NetworkDelivery delivery, TClientIdListType& clientIds) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Messaging/NetworkMessageManager.cs:641) Unity.Netcode.NetworkMessageManager.SendMessage[T] (T& message, Unity.Netcode.NetworkDelivery delivery, System.UInt64 clientId) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Messaging/NetworkMessageManager.cs:814) Unity.Netcode.NetworkConnectionManager.SendMessage[T] (T& message, Unity.Netcode.NetworkDelivery delivery, System.UInt64 clientId) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Connection/NetworkConnectionManager.cs:1282) Unity.Netcode.NetworkBehaviour.NetworkVariableUpdate (System.UInt64 targetClientId, System.Int32 behaviourIndex) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkBehaviour.cs:914) Unity.Netcode.NetworkBehaviour.VariableUpdate (System.UInt64 targetClientId) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkBehaviour.cs:860) Unity.Netcode.NetworkBehaviourUpdater.NetworkBehaviourUpdate () (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkBehaviourUpdater.cs:57) Unity.Netcode.NetworkBehaviourUpdater.NetworkBehaviourUpdater_Tick () (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkBehaviourUpdater.cs:129) Unity.Netcode.NetworkTickSystem.UpdateTick (System.Double localTimeSec, System.Double serverTimeSec) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Timing/NetworkTickSystem.cs:102) Unity.Netcode.NetworkTimeSystem.UpdateTime () (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Timing/NetworkTimeSystem.cs:141) Unity.Netcode.NetworkManager.NetworkUpdate (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkManager.cs:57) Unity.Netcode.NetworkUpdateLoop.RunNetworkUpdateStage (Unity.Netcode.NetworkUpdateStage updateStage) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkUpdateLoop.cs:192) Unity.Netcode.NetworkUpdateLoop+NetworkPreUpdate+<>c.b__0_0 () (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.9.1/Runtime/Core/NetworkUpdateLoop.cs:239)

My manifest.json

{ "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", "com.unity.addressables": "1.21.19", "com.unity.ai.navigation": "1.1.5", "com.unity.cinemachine": "2.10.0", "com.unity.editorcoroutines": "1.0.0", "com.unity.formats.fbx": "4.2.1", "com.unity.ide.visualstudio": "2.0.22", "com.unity.ide.vscode": "1.2.5", "com.unity.inputsystem": "1.7.0", "com.unity.localization": "1.4.5", "com.unity.memoryprofiler": "1.1.0", "com.unity.multiplayer.tools": "1.1.0", "com.unity.netcode.gameobjects": "1.9.1", "com.unity.nuget.newtonsoft-json": "3.2.1", "com.unity.postprocessing": "3.4.0", "com.unity.progrids": "3.0.3-preview.6", "com.unity.services.analytics": "5.0.2", "com.unity.services.authentication": "3.2.0", "com.unity.services.lobby": "1.2.1", "com.unity.services.relay": "1.0.5", "com.unity.services.vivox": "16.3.0", "com.unity.textmeshpro": "3.0.6", "com.unity.timeline": "1.7.6", "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.6", "com.unity.transport": "2.1.0", "com.unity.ugui": "1.0.0", "com.veriorpies.parrelsync": "https://github.com/VeriorPies/ParrelSync.git?path=/ParrelSync", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.audio": "1.0.0", "com.unity.modules.cloth": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.physics2d": "1.0.0", "com.unity.modules.screencapture": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.terrainphysics": "1.0.0", "com.unity.modules.tilemap": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.modules.umbra": "1.0.0", "com.unity.modules.unityanalytics": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0", "com.unity.modules.unitywebrequestassetbundle": "1.0.0", "com.unity.modules.unitywebrequestaudio": "1.0.0", "com.unity.modules.unitywebrequesttexture": "1.0.0", "com.unity.modules.unitywebrequestwww": "1.0.0", "com.unity.modules.vehicles": "1.0.0", "com.unity.modules.video": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" } }

Laumania commented 1 month ago

Now I reverted back to NGO 1.8.1 as the ONLY change and now it works again.

So something is clearly wrong :)

Ufabsther commented 1 month ago

Hello ! After reading through the code, it is likely that 700 : var prevVal = previousValue[i]; is set to a null value in the case of non initiliazed networkvariable. Could you all tell us more about the lifetime of the incriminated variable ? Were they on prefabs or are they runtime initialized ?

Best regards

Laumania commented 1 month ago

@Ufabsther Here is my behavior that is having this issue. Commented out code that is for sure not relevant for this to attempt to keep is down for easier to understanding, but kept most code in.

This script is on my Player prefab, which are obviously instantiated when a player joins.

As mentioned, the exact same code works fine in 1.8.1.

Errors start happening when I press HOME or END as you can see in Update() its how I currently change character.

public class PlayerCustomizationController : NetworkBehaviour
{
    [SerializeField]
    private GameObject _bodyRootNode;
    [SerializeField]
    private CharacterDefinition _defaultCharacterDefinition;
    [SerializeField]
    private PlayerAnimationController _playerAnimationController;
    [SerializeField]
    private PlayerCharacterController _playerCharacterController;

    private GameObject _currenCharacterGameObject;
    private IList<CharacterDefinition> _characterDefinitions;
    private IDictionary<string, CharacterDefinition> _characterDefinitionsDictionary;
    private int _currentCharacterDefinitionIndex;

    private NetworkVariable<FixedString64Bytes> _playerCharacterDefinitionId = new NetworkVariable<FixedString64Bytes>(new FixedString64Bytes(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);

    private Transform _currentCharacterCameraPositionTransform;

    private void Start()
    {
        Preconditions.CheckNotNull(_bodyRootNode);
        Preconditions.CheckNotNull(_defaultCharacterDefinition);
        Preconditions.CheckNotNull(_playerAnimationController);
        Preconditions.CheckNotNull(_playerCharacterController);
    }

    private void Update()
    {
        if (!IsOwner)
            return;

        //Note: A way to test logic to switch characters easially while testing
        if (Keyboard.current.endKey.wasPressedThisFrame)
        {
            SelectNextCharacter();
        }
        else if (Keyboard.current.homeKey.wasPressedThisFrame)
        {
            SelectPreviousCharacter();
        }
    }

    private void LateUpdate()
    {
        if (_currentCharacterCameraPositionTransform != null)
        {
            _playerCharacterController.PlayerCameraRoot.position = _currentCharacterCameraPositionTransform.position;
        }
    }

    private void SelectNextCharacter()
    {
        if (_currentCharacterDefinitionIndex < _characterDefinitions.Count-1)
            _currentCharacterDefinitionIndex++;
        else
            _currentCharacterDefinitionIndex = 0;

        SetCharacterDefinitionId(_characterDefinitions[_currentCharacterDefinitionIndex].Id);
    }

    private void SelectPreviousCharacter()
    {
        if (_currentCharacterDefinitionIndex > 0)
            _currentCharacterDefinitionIndex--;
        else
            _currentCharacterDefinitionIndex = _characterDefinitions.Count - 1;

        SetCharacterDefinitionId(_characterDefinitions[_currentCharacterDefinitionIndex].Id);
    }

    private void InitializeRandomCharacter()
    {
        SetCharacterDefinitionId(_characterDefinitions[UnityEngine.Random.Range(0, _characterDefinitions.Count())].Id);
    }

    private void SetupCharacter(string characterDefinitionId)
    {
        if (string.IsNullOrEmpty(characterDefinitionId))
            characterDefinitionId = _defaultCharacterDefinition.Id;

        if (_characterDefinitionsDictionary.ContainsKey(characterDefinitionId) == false)
        {
            characterDefinitionId = _defaultCharacterDefinition.Id;
            Debug.LogWarning($"Could not find character '{characterDefinitionId}', falling back to default.");
        }

        if (_characterDefinitionsDictionary.TryGetValue(characterDefinitionId, out var foundDefinition))
        {
            if (_currenCharacterGameObject != null)
            {
                DestroyImmediate(_currenCharacterGameObject);
            }

            _currenCharacterGameObject = Instantiate(foundDefinition.CharacterPrefab, _bodyRootNode.transform);
            _currenCharacterGameObject.RemoveComponent<Animator>();
            _playerAnimationController.Animator.avatar = foundDefinition.CharacterAvatar;
            _playerAnimationController.Animator.Rebind();
            SetupNeckConstraint(_currenCharacterGameObject, foundDefinition);
        }
        else
            Debug.LogError($"CharacterDefinition Id '{characterDefinitionId}' not found", this);
    }

    private void SetupNeckConstraint(GameObject currentCharacterGameObject, CharacterDefinition characterDefinition)
    {
        //Not important
    }

    [Command("fm-player-character", "Change the player character", MonoTargetType.All)]
    private void SetCharacterDefinitionId([CharacterSuggestor] string characterDefinitionId)
    {
        if (IsOwner)
        {
            if (string.IsNullOrEmpty(characterDefinitionId) || _characterDefinitionsDictionary.ContainsKey(characterDefinitionId) == false)
            {
                characterDefinitionId = _defaultCharacterDefinition.Id;
                //Debug.LogWarning($"Could not find last used character '{characterDefinitionId}', falling back to default.");
            }

            _playerCharacterDefinitionId.Value           = new FixedString64Bytes(characterDefinitionId);
            GlobalSettings.LastUsedCharacterDefinitionId = characterDefinitionId;
        }
    }

    public override void OnNetworkSpawn()
    {
        base.OnNetworkSpawn();

        _characterDefinitions           = ModManager.Instance.CharacterDefinitions.ToList();
        _characterDefinitionsDictionary = _characterDefinitions.ToDictionary(x => x.Id, x => x);

        _playerCharacterDefinitionId.OnValueChanged += (prevValue, newValue) =>
        {
            SetupCharacter(newValue.ToString());
        };

        if (IsOwner)
        {
            SetCharacterDefinitionId(GlobalSettings.LastUsedCharacterDefinitionId);
        }
        else
        {
            SetupCharacter(_playerCharacterDefinitionId.Value.ToString());
        }
    }

    public Transform CurrentCharacterCameraPositionTransform => _currentCharacterCameraPositionTransform;
}
matus-d-ui42 commented 4 weeks ago

hi, i just upgraded to 1.9.1, found the solution. the null part is not in the values, but in the AreEqual delegate, which is null for the byte type since its not initialized anywhere @Ufabsther just add

NetworkVariableSerialization<byte>.AreEqual = NetworkVariableSerialization<byte>.ValueEquals;

to Unity.Netcode.NetworkVariableSerializationTypes.InitializeIntegerSerialization

Edit: i mean, i could be absolutely wrong about the correctness/placement of the code, but it started working, so at least the reason is correct :)

Edit: also please make sure it won't happen when we upgrade to 2.0 in the future :D

sakul-the-one commented 3 weeks ago

This is the Solution I found 12 hours ago was creating a new struct, that fixed it and made it worked for me. Or you just use the Solution of @matus-d-ui42

But here is an example, where Im using it for Player ID:

private NetworkVariable<NFixedString32ID> PlayerID = new NetworkVariable<NFixedString32ID>(new NFixedString32ID { _id = "WrongID" }, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);

public struct NFixedString32ID : INetworkSerializable //NFixedString32ID stands for New Fixed String 32 bytes used for Identification
 {

     public FixedString32Bytes _id;

     public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
     {
         serializer.SerializeValue(ref _id);
     }
     public string Value()
     {
         return _id.ToString(); //The same as ToString()
     }
     public void SetValue(string newValue)
     {
         _id = newValue;
     }
     public override string ToString()
     {
         return _id.ToString();
     }
 }
gabrielsotomay commented 2 weeks ago

Disclaimer: I don't know what I'm doing, but I got things working for myself.

I added on to the fix by @matus-d-ui42 to fix two other errors I got afterwards that unfortunately I didn't record but were along the lines of not finding a Serializer and not using TryBeginRead() before using ReadByte().

I was able to fix it by adding the following to Unity.Netcode.NetworkVariableSerializationTypes.InitializeIntegerSerialization: NetworkVariableSerialization<byte>.Serializer = new UnmanagedTypeSerializer<byte>(); NetworkVariableSerialization<byte>.AreEqual = NetworkVariableSerialization<byte>.ValueEquals; Also had to add reader.TryBeginRead(i); in INetworkVariableSerializer outside of the "if" in the for loop like this:

for (var i = 0; i < value.Length; ++i)
{
     reader.TryBeginRead(i);
     if (changes.IsSet(i))
     {
      reader.ReadByte(out ptr[i]);
     }
}
N1C0U commented 2 weeks ago

Same problem here :(

imgogole commented 2 weeks ago

Just upgraded as well from 1.9.1 to 1.8.1 and it seems working fine for me.

qjonathan commented 2 weeks ago

having the same issue, @matus-d-ui42 solution seems to fix it for me at the moment https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues/2920#issuecomment-2143039558

ShadauxCat commented 1 week ago

Hi everyone! A fix for this issue is in progress, but I just wanted to mention a couple of workarounds that can be used in the short term until the fix gets released:

First option: put this attribute on any class or method anywhere in your codebase:

[GenerateSerializationForType(typeof(byte))]

Second option: put this class anywhere in your codebase:

class Unused : NetworkBehaviour
{
    NetworkVariable<byte> workaround;
}

It doesn't need to be referenced anywhere, it just has to exist.

Either one of those will trigger the codegen to correctly generate serialization code for byte

Laumania commented 1 week ago

Thanks @ShadauxCat !

However, it's not only an issue with bytes - actually it seem to be more a problem for us using FixedString.

Ufabsther commented 1 week ago

According to @ShadauxCat , FixedString are serialized as byte therefore the underneath issue was byte serialization

Laumania commented 1 week ago

According to @ShadauxCat , FixedString are serialized as byte therefore the underneath issue was byte serialization

Ok fair - make sense :)