Sync Var Hook Changing Variables, but not calling functions...

I’m making a switching weapons script but I can’t seem to find anywhere where it tells me how to do this or what I’m doing wrong. It would be a huge help if someone could look at my code and tell me what I’m doing wrong. Sorry if it’s messy ive been kinda rushing. Thanks!

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class WeaponManager : NetworkBehaviour
{

    private string WeaponLayerName = "WeaponLayer";



    public int[] equippedWeapons;

    [SerializeField]
    private Transform weaponHolder;

    [SerializeField]
    private PlayerWeapon primaryWeapon;

    [SerializeField]
    private PlayerWeapon secondaryWeapon;

    [SerializeField]
    private PlayerWeapon specialWeapon;

    [SerializeField]
    private PlayerWeapon MeleeWeapon;

    [SyncVar (hook = "SwitchWeaponCallback")]
    private int currentWeaponIndex = 0;

    private PlayerWeapon currentWeapon;
    private WeaponGraphics currentGraphics;
    public bool isSwitchingWeapon = false;



    bool canSwitchAgain = true;

    private PlayerWeapon weaponToLoad;

    private bool firstEquip = true;


    // Use this for initialization
    void Start()
    {
        //EquipWeapon(equippedWeapons[0]);
        isSwitchingWeapon = false;
    }


    public PlayerWeapon GetCurrentWeapon()
    {
        return currentWeapon;

    }
    public WeaponGraphics GetCurrentGraphics()
    {

        return currentGraphics;

    }
    public override void OnStartLocalPlayer()
    {
        EquipWeaponStart(currentWeaponIndex);

    }

    public override void OnStartServer()
    {
        base.OnStartServer();
        firstEquip = true;
        currentWeaponIndex = equippedWeapons[0];
    }
    public void SwitchWeaponCallback(int _weapon)
    {
        print("Changed");
        currentWeaponIndex = _weapon;
        EquipWeapon(currentWeaponIndex);
    }

    public override void OnStartClient()
    {

        EquipWeaponStart(equippedWeapons[0]);
    }
 


   




    void EquipWeaponStart(int _weapon)
    {

       
           
        if (_weapon == currentWeaponIndex) return;
               
           
       

        if (firstEquip)
            firstEquip = false;



        gameObject.GetComponent<PlayerShoot>().aiming = false;

        StopCoroutine(gameObject.GetComponent<PlayerShoot>().Reload());





     
        StartCoroutine(DrawTimer(_weapon));
       
    }


    void EquipWeapon(int _weapon)
    {
        if (_weapon == currentWeaponIndex) return;
        print("switch" + _weapon);

     
           // CmdEquipWeapon(_weapon);
            DoEquipWeapon(_weapon);
       

    }




    IEnumerator DrawTimer(int _weapon)
    {

        while (gameObject.GetComponent<PlayerShoot>().fireRateTimer > 0)
        {
            yield return null;
        }

        while (isSwitchingWeapon == true)
        {
            yield return null;
        }
        canSwitchAgain = false;
        isSwitchingWeapon = true;

        gameObject.GetComponent<Player>().SetAnimationParameter("IsSwitchingWeapon", 3, 0, 0, true, true);
        if (currentWeapon != null)
            yield return new WaitForSeconds(currentWeapon.putAwayTime);

        EquipWeapon(_weapon);
        canSwitchAgain = true;

        if (currentWeapon != null)
            yield return new WaitForSeconds(currentWeapon.drawTime);

        gameObject.GetComponent<Player>().SetAnimationParameter("IsSwitchingWeapon", 3, 0, 0, false, true);
        isSwitchingWeapon = false;



    }
  void DoEquipWeapon(int _weapon)
    {

     


        currentWeaponIndex = _weapon;

        if (currentWeapon != null)
        {

            currentWeapon.graphics.SetActive(false);
        }

        switch (_weapon)
        {


            case 0:
                // no weapon
                currentWeapon = null;
                break;
            case 1:
                // primary
                currentWeapon = primaryWeapon;
                break;
            case 2:
                // secondary
                currentWeapon = secondaryWeapon;
                break;
            case 3:
                // special
                currentWeapon = specialWeapon;
                break;
            case 4:
                // Melee
                currentWeapon = MeleeWeapon;
                break;
        }



       

        currentGraphics = currentWeapon.graphics.GetComponent<WeaponGraphics>();


        if (isLocalPlayer)
        {
            Util.SetLayerRecursively(currentWeapon.graphics, LayerMask.NameToLayer(WeaponLayerName));
        }



        currentWeapon.graphics.SetActive(true);
        currentWeapon.spreadFactorSet = currentWeapon.spreadFactor;
        gameObject.GetComponent<Player>().SetAnimationParameter("CurrentWeapon", 2, 0f, currentWeaponIndex, false, true);
        gameObject.GetComponent<PlayerShoot>().fireRateTimer = 0;
        gameObject.GetComponent<PlayerShoot>().aiming = false;
        if (GetComponent<PlayerSetup>().playerUIInstance != null)
        {

            GetComponent<PlayerSetup>().playerUIInstance.GetComponent<PlayerUI>().crossHair.SetActive(true);
        }

    }

  void Update()
    {
        if (!isLocalPlayer)
            return;

       


        if (Input.GetKeyDown("1"))
        {
            EquipWeaponStart(equippedWeapons[0]);
           
        }
        if (Input.GetKeyDown("2"))
        {
             EquipWeaponStart(equippedWeapons[1]);
           
        }

        if (Input.GetKeyDown("3"))
        {
             EquipWeaponStart(equippedWeapons[2]);
        }
        if (Input.GetKeyDown("4"))
        {
             EquipWeaponStart(equippedWeapons[3]);
           
        }
    }


    public void loadAmmoStart()
    {

        for (int i = 0; i < equippedWeapons.Length; i++)
        {
            SetAmmo(i);
        }
    }

    public void SetAmmo(int _weapon)
    {



        switch (_weapon)
        {
            case 0:
                // no weapon
                weaponToLoad = null;
                break;
            case 1:
                // primary
                weaponToLoad = primaryWeapon;
                break;
            case 2:
                // secondary
                weaponToLoad = secondaryWeapon;
                break;
            case 3:
                // special
                weaponToLoad = specialWeapon;
                break;
            case 4:
                // Melee
                weaponToLoad = MeleeWeapon;
                break;
        }


        if (weaponToLoad != null)
            weaponToLoad.currentAmmo = weaponToLoad.magazineSize;

    }


}

Is the value of currentWeaponIndex being changed?
If currentWeaponIndex is being changed is SwitchWeaponCallback being called on the server? Or just not clients?

The way hooks work is that the new value must also be set within the hook or else the value wont be set on the client(don’t ask me why). So…
currentWeaponIndex = weapon
should be part of your SwitchWeaponCallback

Also, SyncVar callbacks are not called during initialization when OnSerialize/Desierialize w/ initialState==true. So if you want the hook called when a client connects you must call it yourself (I do this during Start)

It seems the value is set, but the function isnt called

A trick working for me, i have a lot of similar problems working with syncvars and rpc cmd functions.

1- Put empty object in scene (named “TriggerSpawnGameObject” or name you want)
2- Put InputField in scene, out of screen, not visible to game screen( Name “InputTrigger”)
3- In TriggerSpawnGameObject put new script (Named “ScriptTriggerFunction”)

   [SyncVar(hook = "Trigger")]
    public string stringTrigger;
    public InputField inputTrigger;

    public void Trigger(string msj)
    {
        inputTrigger.text = msj;
    }
    void Start ()
    {
        inputTrigger = GameObject.Find("InputTrigger").GetComponent<InputField>();
    }

    void Update ()
    {
  
    }

4 - Create new gameObject and registrer how spawned object in server(prefab named “PrefabTrigger”)
5- In prefabTrigger, add new script(named " ScriptGameObjectTrigger")

v
oid Start ()
    {
        if(isServer)
        {
            GameObject.Find("TriggerSpawnGameObject").GetComponent<ScriptTriggerFunction>().stringTrigger = Random.Range(0, 1000).ToString();
            Destroy(gameObject, 3);
        }
    }

At this moment we have an object with function to change a syncvar string on server, that make change that value on all clients, and input field on game with new value.
We can use this gameobject to say all clients do something when any call (Client or server)

[Command]
    public void CmdSpawnTrigger()
    {
        var go = Instantiate(prefabTrigger);
        NetworkServer.Spawn(go);
    }

For example, in your script to switch weapons.

public InputField inputTrigger;
    public override void OnStartLocalPlayer()
    {
        base.OnStartLocalPlayer();
        inputTrigger = GameObject.Find("InputTrigger").GetComponent<InputField>();
        inputTrigger.onValueChanged.AddListener(delegate { FunctionToCall(); });
        //Maybe you need call IEnumerator to wait for sure all data its synchronized, then call
       CmdSpawnTrigger(/* info to share if needed */);
    }

    public void FunctionToCall()
    {
        // YourCode
    }

This is not for sync variables, you say value is set, but function not called.
When you change any syncvar variable, you can spawn prefabTrigger after change they value.
Then, after set value, a FunctionToCall its called on all clients, you can get do you want in that function.
Or you can send info to allplayers on prefabTrigger script.

public int indexWeapon;
public bool onlyTrigger = true;
void Start ()
    {
        if(isServer)
        {
          if(onlyTrigger)
{
            GameObject.Find("TriggerSpawnGameObject").GetComponent<ScriptTriggerFunction>().stringTrigger = Random.Range(0, 1000).ToString();
} else
{
           GameObject.Find("TriggerSpawnGameObject").GetComponent<ScriptTriggerFunction>().stringTrigger = indexweapon.toString();
}
Destroy(gameObject, 3);
        }
    }
public void GiveMeInfo(int number)
{
    indexWeapon = number;
    onlyTrigger = false;

}

and for pass info to prefabTrigger:

[Command]
    public void CmdSpawnTrigger(int number)
    {
        var go = Instantiate(prefabTrigger);
        go.GetComponent<ScriptGameObjectTrigger>().GiveMeInfo(number);

        NetworkServer.Spawn(go);
    }

    //CmdSpawnTrigger(your int info to share)

Now all clients have shared int on inputfield, and all clients call FunctionTocall.
I dont know if I good explain, i have bad english capacities :wink: but with this method I solved a lot of problem with sync vars and make all clients know when something happening and what do when something happend.
For example, when a client connect, the client spawn prefabTrigger for say all clients new player its connected.
I hope this helps.

I can´t edit , change numberToShare to number on line 5 on last script
the empty gameObject in scene, have networkIdentity and should be spawned by server on start server (Networkserver.spawnObjects())

thanks but I actually figured it out