SyncVar not updating on clients when used with hook

Im working through this tutorial https://unity3d.com/de/learn/tutorials/topics/multiplayer-networking
SyncVar is used to synchronize the current health values applied on the server with the clients. Because the healthbars are not synchronized hook= is used to update them on the clients in chapter 14. As soon as hook= is used the currentHealth values are no longer updated in (the inspector of) the clients but only in the servers. The healthbars work correctly.

This is the code so far:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
        {
            return;
        }

        currentHealth -= amount;
        if(currentHealth <= 0)
        {
            currentHealth = 0;
            RpcRepawn();
        }

    }

    [ClientRpc]
    void RpcRepawn()
    {
        if(isLocalPlayer)
        {
            transform.position = Vector3.zero;
        }
    }

    void OnChangeHealth(int currentHealth)
    {
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}

I dont understand why the values in the inspector wont update anymore. As far as I undestood this, SyncVars are synchronized with the clients and then when they change the function is called.
If I include a print if the currentHealth is below a threshold in TakeDamage() it wont happen, which means that the value indeed does not update.
If I reconnect the client the players health values are correct. If the health values change again they wont update until the client reconnects.

You should manually assign the new value of health.
There is an error on official unity post.

void OnChangeHealth(int newHealth)
    {
        currentHealth = newHealth; //fix
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
1 Like

If this is intended behaviour it really should be mentioned in the docs.

https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute-hook.html

1 Like

Thanks for the reply. What do you mean by “error”? Unity Engine bug or error in the tutorial? If this is intended then im confused why because its still a SyncVar isnt it?

https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute-hook.html

It also seems that the docs and the tutorial are not consistent. In the docs there is written “… a function to be called when the sync var changes value on the client”. So for me this means that the server recognizes a change and then synchronizes the new value with the clients. Then the assigned function is called only on the clients. While in the tutorial there is written " These functions are invoked on the Server and all Clients when the value of the SyncVar changes".

Also if the doc is right, how comes that the function is called on the client even though the value did not change on the client?

The value doesn’t assign when hook is called. I gave you a fix and an explanation already.

Yeah, it really seems like SyncVar hooks are broken. The ideal behavior – and what the documentation implies – is that a hook should act like what we’d call a “callback” or “delegate” in many other coding situations. That is, when the value of a SyncVar changes, the hook function fires to execute follow-up actions in response to that change.

But if a SyncVar Hook blocks the updating of the value or fires before it’s actually updated (which seems to be the case), then it defeats the whole purpose of the hook in my view. Manually updating the SyncVar value within the hook may work as a fix, but it’s a hacky one and doesn’t really support good Networking practice. If the whole purpose of SyncVars is that they’re consistently and automatically synced across all clients, developers shouldn’t be able to accidentally or intentionally block that syncing just by adding a hook.

A SyncVar hook doesn’t update the SyncVar itself to allow you the opportunity to still use the old value in your hook function. For example if a SyncVar represented a player’s score, and the server increased the score by +2, the client would get the SyncVar hook function called and still has the old score in the SyncVar. So the client can then subtract the old score from the new one, and can throw some cool “+2 Points!” thing on the screen, then update the client version of the SyncVar with the new value.

Otherwise either the client would need to store another copy of the SyncVar value, or the server would need to call a separate CllientRpc, to get something as simple as telling the client how much a SyncVar has changed instead of just its current value.

I believe this is as designed behavior, but I don’t believe it is in any of the documentation (UNET HLAPI docs are not very thorough).

Thanks for the explanation – I can see the value in that. Personally, I think it’s less valuable than having variables with reliable consistent values across all network instances, especially when there are other ways to track variable changes. But the main problem remains that this behavior is not what Unity Documentation or Tutorials would lead anyone to expect. I’ve logged feedback on the SyncVar hook Script Reference documentation page to highlight this.

Also, further complicating this is that if a game instance is acting as a Host (i.e. both Server and Client), I believe the SyncVar value for that instance will already be updated when the hook fires for that Client – but not for any other Guests/Clients. This leads to even more divergent and confusing behavior across the Network. Running one instance as a Host is the most common setup for people who are just learning UNET, so this seems like a pretty big stumbling block.

Thanks for the thread. I spent a few hours trying to figure out why SyncVar wasn’t working until I found this thread. This really should be added to the documentation for SyncVar and SyncVarAttribute.hook, especially when the attribute changes the way SyncVar works and creates inconsistency.

1 Like

Thank you soooo much for this explanation and fix - luckily I started looking online as soon as I started to get the weird behavior. Hopefully a Unity Mod sees this and updates the tutorial - it is a great tutorial until it gets to this point.

Unet has been deprecated, so don’t expect any documentation or tutorial updates for it.

This is absolutely backwards and unintuitive!

It needs to be documented somewhere, for the love of everything holy.

Thank you for this thread and the investigative work.