Can't trace the MissingReference

Hello folks,

I am encountering a weird MissingReference exception with Coherence that I can’t piece together where it comes from.

This is my code Script - Pastebin.com

This is the error it produces Error - Pastebin.com

What happens is that I have a local player (LP) and a remote player (RP). LP shoots at RP and the flow is that in the script NetworkApplyDamage is called by LP. ApplyDamage is a synced method that is set to AuthorityOnly in CoherenceSync.
When the source client of RP does apply the damage on their end, I call the UpdateGUI method (set to All in CoherenceSync because I couldn’t find an Other option) with Other flag in the command itself.

Note that there are a bunch of Debug Logs in the methods to determine where it fails. So far, all of them except the UpdateGUI one seem to print out. So the one from NetworkApplyDamage and ApplyDamage appear and on the correct clients. The last one, doesn’t and is when the error appears. The error appears on RP’s source client.

Is there any way I can tell what exactly is the missing reference? I am inferring it is an image given the error. I went through the player object on RP’s source client and it didn’t have a single null or missing reference. The only one affected by this method is a field that is filled in the inspector and it is always there and never removed.

Thanks in advance

Hey, so this doesn’t look to me like a coherence issue. It looks like there was a UI element that was deleted but there’s still code referencing it. The callstack here shows that:

When hpController.DecreaseAmount(_damage, false);is called, it changes something in GlobalVariable which is hooked into UpdateHUDStatBar which has a reference to a deleted image. I suspect that maybe that bar existed in another context and was destroyed but the global variable is referencing old stuff and wasn’t updated. So, I would look here and see when any of those classes could be referencing something old and destroyed.

Thanks a lot @cary I was checking a different script that had an image reference too. At first I thought it wasn’t a Coherence issue too. But maybe I am misunderstanding how things work.

The way I have this setup is that I have a ScriptableObject (SO) called Current HP. Inside there is an OnValueChanged event that fires to update the local GUI.

Meanwhile, I have another SO called StatController that does the increase/decrease of HP when a player receives damage.

Finally, I have a MonoBheaviour called NetworkHP that is called by a damaging entity (since Coherence commands won’t run on SOs) to then call StatController which then modifies Current HP. When Current HP is modified, the OnValueChanged event is fired and the GUI listening gets updated.

Now, why is Coherence not sending a command, or the command fell short because of a missing exception in the GUI script that is hooked to an event completely outside of anything related to its scope? The chain of execution goes NetworkHP (where the command lives) > Stat Controller > Current HP.

Let me put it another way, I have the game setup in two scenes; connection and game. Connection creates a room for players to join and then transitions to game. Game scene already has the GUI and player placed in the scene manually and they aren’t spawned in. If I run the game scene offline, this error doesn’t stop the command (UpdateGUI that seems to produce the missing exception). In fact, if I change the command from Other to All so it runs locally, the command actually runs and the exception does happen after it. But they aren’t predicated on each other. When I am online, the case is different and all of a sudden, the command is stopped due to this exception that has nothing to do with it.

What am I missing?

Bump and an update.

So I checked the code further and added a debug log in an Update to check if the object being referenced is ever destroyed or the value is null as CoherenceSync claims when processing the command (even though logically, this shouldn’t concern CoherenceSync as this is a response to an event fired outside of its chain of execution).

And surprisingly, it doesn’t lose the reference (i.e. never turns null) and it never gets destroyed (it is present in the scene at all times with the correct value).

Here is a screenshot below as proof

The 4.7K messages saying “HP Bar Forground” (excuse the typo) is literally a Unity Update running “Debug.Log(image)” which is the exact object referenced in the exception.

Note there are no nulls, no missing references, nothing. Note the time too. The Update is running BEFORE, DURING and AFTER the command was issued.

Anyone can help with this?

Thanks in advance

Another bump.

The exact method that causes the error is quoted below:

    public void UpdateHUD(float _currentAmount)
    {
        if(hudImage == null)
        {
            hudImage = GetComponent<Image>();

            if (hudImage == null)
                Debug.Log("I couldn't grab a reference no Image component exist here");
        }

        hudImage.fillAmount = _currentAmount / maxAmount.Value;
    }

if I comment out the content of the method and leave it empty, CoherenceSync will fire the command normally. Now if I uncomment the content of the method, with the above code, CoherenceSync still raises a null exception and the Debug.Log() doesn’t even fire. I am looking at the object in the inspector while this is happening and the inspector shows the object has an Image component. The image component didn’t get destroyed, the object itself didn’t get destroyed. The object didn’t get deactivated. It is intact and while the Image component is present and the reference is not null (I had Debug mode active on the inspector so I can see the reference in front of my eyes) CoherenceSync is firing the error that there is a missing reference.

Can anyone explain this to me?

Hey!

Let me explain. This is an extremely simplified model of how we handle commands received from the network:

void Update() {
  var commandsReceived = GetCommandsFromLastPacket();
  foreach(var command in commandsReceived) {
    CoherenceSync sync = FindReceiverForCommand(command);
    ExecuteCommand(command, sync);
  }
}

void ExecuteCommand(Command command, CoherenceSync sync) {
    try {
      sync.bakedCode.ExecuteCommand(command);
    } catch (Exception e) {
      Debug.LogError($"ClientCore: Exception in handler. Exception: {e}");
    }
}

The sync.bakedCode.ExecuteCommand(command); is where your ApplyDamage function would be invoked.

As you can see, for every command received, we execute that command in a try/catch block. The reason for this is, if your command code throws an exception, we can still continue operating and handle other commands in the queue.

Your ApplyDamage(simplified) looks as follows:

public void ApplyDamage(int _damage)
{
    // ... some operations ...
 
    hpController.DecreaseAmount(_damage, false);


    cacher.sync.SendCommand<NetworkHP>(nameof(UpdateGUI),
      Coherence.MessageTarget.Othe, hpController.guiRatio);
}

We could therefore present our code as:

void ExecuteCommand(Command command, CoherenceSync sync) {
    try {
      // Inlined `ApplyDamage` code

      // ... some operations ...
 
      hpController.DecreaseAmount(_damage, false);


      cacher.sync.SendCommand<NetworkHP>(nameof(UpdateGUI),
        Coherence.MessageTarget.Othe, hpController.guiRatio);
    } catch (Exception e) {
      Debug.LogError($"ClientCore: Exception in handler. Exception: {e}");
    }
}

Your question is, why is the command from the last line of ApplyDamage is not being sent. The reason for it is, your hpController.DecreaseAmount(_damage, false); throws an exception, which is caught by our try/catch block.

Virtually it’s equivalent to:

void ExecuteCommand(Command command, CoherenceSync sync) {
    try {
      // Inlined `ApplyDamage` code

      // ... some operations ...
 
      throw null;

      cacher.sync.SendCommand<NetworkHP>(nameof(UpdateGUI),
        Coherence.MessageTarget.Othe, hpController.guiRatio);
    } catch (Exception e) {
      Debug.LogError($"ClientCore: Exception in handler. Exception: {e}");
    }
}

The cacher.sync.SendCommand can never execute because exception stops the execution of this functions and starts unwinding the stack.

You’ve also noticed that commenting out your UpdateHUD code results in command being sent. That’s because you’re preventing the exception from happening, and the execution of ApplyDamage can continue.

You can achieve the same by applying your own try/catch block in the ApplyDamage:

public void ApplyDamage(int _damage)
{
    // ... some operations ...
 
    try {
      hpController.DecreaseAmount(_damage, false);
    catch (Exception e) {
      Debug.LogError($"Exception in hpController.DecreaseAmount: {e}");
    }

    cacher.sync.SendCommand<NetworkHP>(nameof(UpdateGUI),
      Coherence.MessageTarget.Othe, hpController.guiRatio);
}

There’s nothing that we can do to fix that problem. You need to find out why your code is throwing an exception, which is derailing the execution of ApplyDamage.

Hope that helps!

@Filip

Thank you very much for the extensive explanation. Really I appreciate it.

However there is a wrong assumption here. the line hpController.DecreaseAmount(_damage, false); can never throw an exception of a null reference of an object it doesn’t have context for. This can never throw a missing reference/null reference since its chain ends with a raised event.

And we can’t say maybe the event is empty because that can’t throw an exception as it is initialized like this:
public event Action<T> onValueChanged = delegate { };

The code execution path doesn’t make sense for this command to receive an exception. The code goes like this NetworkHP (Monobehaviour) > StatController (ScriptableObject) > CurrentHP (ScriptableObject) > onValueChanged.Invoke.

Then on another object listening to this event UpdateHUD runs. Which then Coherence is saying throws a missing reference exception. So why in the world, this object, where the command has nothing to do with or have any awareness of is getting a null, when it is responding to an event already fired correctly?

I will say in my previous 2 messages I showed that running the exact same scene, exact same setup, exact same everything with one exception – Coherence is offline. No nulls, no Missing Reference Exception.

Moreover, I changed the code in the specific method that causes the thrown exception to the below:

public void UpdateHUD(float _currentAmount)
{
    if(hudImage.Equals(null))
    {
        hudImage = GetComponent<Image>();

        if (hudImage.Equals(null))
        {
            Debug.Log("I couldn't grab a reference no Image component exist here");
            return;
        }
    }

    hudImage.fillAmount = _currentAmount / maxAmount.Value;
}

When offline, nothing happens, the whole executes, no debug log from that method. This means the reference isn’t missing or null.
When online, again, Coherence throws an exception. If the reference is missing or null, it should exit early and never have to deal with the code producing the null. But no, it doesn’t. No Debug.Log saying the above, the code doesn’t fire properly and Coherence throws the exception.

And I am sure I am on the correct client when looking at this. I will ask one thing, is there a possibility Coherence could be caching commands or something – maybe at a previous point in time this was a missing exception and it got cached somewhere?

As per code shown above, we’re not throwing any exceptions on command handling path.

It may seem like it’s our code that is the error source, because it is our code that invokes your command and logs an error when your code throws an exception. The stacktrace that you’ve provided however is very clear that the exception is happening in your UpdateHUDStatBar.cs, line 28

[Player 2] 20:44:07.155 (coherence) ClientCore: Exception in handler. caller=OnCommand exception=UnityEngine.MissingReferenceException: The object of type ‘UnityEngine.UI.Image’ has been destroyed but you are still trying to access it.

Your script should either check if it is null or you should not destroy the object.

at UnityEngine.Object+MarshalledUnityObject.TryThrowEditorNullExceptionObject (UnityEngine.Object unityObj, System.String parameterName) [0x0006a] in <1e74f08236fb4c1791a523c0bf197e6c>:0

at UnityEngine.Bindings.ThrowHelper.ThrowNullReferenceException (System.Object obj) [0x00010] in <1e74f08236fb4c1791a523c0bf197e6c>:0

at UnityEngine.Behaviour.get_isActiveAndEnabled () [0x00006] in <1e74f08236fb4c1791a523c0bf197e6c>:0

at UnityEngine.EventSystems.UIBehaviour.IsActive () [0x00000] in .\Library\PackageCache\com.unity.ugui@f3fac7af1578\Runtime\UGUI\EventSystem\UIBehaviour.cs:28

at UnityEngine.UI.Graphic.SetVerticesDirty () [0x00000] in .\Library\PackageCache\com.unity.ugui@f3fac7af1578\Runtime\UGUI\UI\Core\Graphic.cs:295

at UnityEngine.UI.Image.set_fillAmount (System.Single value) [0x00013] in .\Library\PackageCache\com.unity.ugui@f3fac7af1578\Runtime\UGUI\UI\Core\Image.cs:502

at UpdateHUDStatBar.UpdateHUD (System.Single _currentAmount) [0x00000] in D:\Projects\Work\LimitBreakers\Assets\Scripts\GUI\Player HUD\UpdateHUDStatBar.cs:28

at (wrapper delegate-invoke) System.Action`1[System.Single].invoke_void_T(single)

at SoArchitecture.GlobalVariable`1[T].set_Value (T value) [0x00023] in Assets\Scripts\Architecture\Global Variables\GlobalVariable.cs:34

at StatController.DecreaseAmount (System.Single _amount, System.Boolean _allowExcess) [0x00018] in Assets\Scripts\Player\Stat Components\New\StatController.cs:67

at NetworkHP.ApplyDamage (System.Int32 _damage, System.Single _hitStunTime, System.Single _blockStunTime, System.Single _absorbStunTime, System.Single _moveSpeedReduction) [0x000cc] in Assets\Scripts\Player\Stat Components\New\NetworkHP.cs:72

What I would suggest is catching the exception in the UpdateHUDStatBar and then using Debug.Break() and Debug.Log(gameObject)to track down the faulty game object.

I’m afraid we won’t be able to provide any more help from here. I’m sure you’ll be able to track this down in no time. Good luck!

@Filip Thank you very much for the clarification.

I have one last question, does Coherence by any chance duplicate scene objects when it starts? Or maybe has like a second scene loaded async when switching scenes or something that would cause duplication of objects?

Because you were absolutely right, the missing reference exception was a Unity thing, an object registered to the CurrentHP event which later was destroyed and of course the subscriber didn’t unsubscribe which of course produced the error because this object was designed to live throughout the entire game session.

However, I dug deeper and found something odd that I was hoping you can explain.

Currently our setup is as follows; we have a connection scene where we create a room and join it. Then we switch the scene to the game scene. The game scene already has the player, camera, etc… in the hierarchy so nothing is spawned when the scene is loaded. The player character is the only Coherence Sync we have in that scene. For some odd reason, every single object in that game scene gets duplicated on the second, third, etc… clients that join the room. The first one is fine, it is always second, third, fourth, etc… and not only do these objects get duplicated, they get deleted after they are duplicated.
So for example we have a Global Volume, Directional Light, Event System, a couple of UIs trees, etc… they all get duplicated after the scene is switched from connection to game and the duplicates (or the original, I am not sure I didn’t test) are deleted and one of them is kept around.

Which as you can see, this duplication and deletion of objects is the cause of the missing exception happening because the code wasn’t accounting for anything duplicated or spawning, they are just there and won’t change till the scene ends.

Any idea what is this about?

Hey, the only thing that coherence instantiates or destroys are prefabs created with the CoherenceSync. So, if you’re seeing things like your lights duplicate then your loading setup is probably loading the scene twice and deleting one of them. The only time that coherence directly deals with duplicates (as in detects and then potentially destroys) is when you’re using Uniqueness but, again, you shouldn’t be seeing things like the UI and the lights affected by this unless they’re part of a prefab with CoherenceSync (and they probably should not be)

Thanks a lot folks. Turns out we had a flaw in our understanding of how bridge works which caused us to load the scene twice when switching. Thank you very much for your patience on this.