SyncList not always syncing to clients

Has anyone experienced this issue?

I use a float SyncList to sync some attributes (health, stamina, ect) from the server to clients and sometimes the value is not synced to the client. I use a struct SyncList for an inventory from server to client and about 20% of the time this does not sync either.

I have tried with and without the hook callback. I have tried manually setting the index to dirty. I have tried reliable and unreliable state update channels. Nothing seems to make a difference.

What I am attempting seems simple and the code is not complex. Is this a UNET bug? Has anyone worked around it successfully? This is on 5.3.4.

1 Like

Could be this bug: BUG: SyncListStruct only works with some file names - Unity Engine - Unity Discussions

1 Like

Yeah. Seems there are multiple strange issues with them… Any workarounds? Really don’t want to have to create a way of syncing arrays by scratch.

1 Like

With limited testing if I mark the indexes dirty every frame it doesn’t seem to miss an update on the client. Of course this is a waste of bandwidth.

For a work around I will try setting the dirty flag for multiple frames if a change occurs on the server through a coroutine but not every frame. Hope this is fixed soon.

Did you guys ever come up with an ideal solution?

I had the same problem, or at least i think i had. I finally was able to fix this. I do not know if this is what you are looking for? I had a lot of struggle to get this going.

Good luck.

Here is the scene i used for the testing:

pic upload

Below is the script(s) i use that works for me. First is the actual SyncListString, which is attached to the UI ChatBox component.

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

public class UNET_SyncList_2 : NetworkBehaviour {

    //[SyncVar]
    public SyncListString myList = new SyncListString();

    private GameObject chatBox;
    private GameObject myText;
    private int before;
    private int after;

    void Start () {
//        print ("Start");
        chatBox.GetComponent<Text>().text += "\n";

        if (isLocalPlayer) {
            Cmd_AddOnServer ();
        }
    }

    [Command]
    public void Cmd_AddOnServer () {
        myText.GetComponent<Text> ().text = "Command";
        print ("Command");
        myList.Add ("LETTER A");
        myList.Add ("LETTER B");
        myList.Add ("LETTER C");

        foreach (string _str in this.myList)
            print (_str);
    }

    public override void OnStartClient() {
        chatBox = UNET_SyncList_2.FindObjectOfType<UNET_SyncList_2>().gameObject;
        myText = GameObject.Find ("TextX");
        myList.Callback += MyCallback;
    }

    private void MyCallback(SyncListString.Operation op, int index) {
        string idx = "";
        if (myList.Count > 0)
        {
            idx = myList[index];
        }
        string str = "Op=" + op.ToString () + " Index=" + idx + " New Array Length=" + myList.Count;
        chatBox.GetComponent<Text>().text += str + "\n";
    }


}

Here is the player script:

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


public class Player : NetworkBehaviour {
    public UNET_SyncList_2 ChatBox;

    // Use this for initialization
    void Start () {
  
    }
  
    // Update is called once per frame
    void Update () {
  
    }


    public override void OnStartClient()
    {
        GameObject.Find ("Button1").GetComponent<Button>().onClick.AddListener(delegate { Btn_Add();});
        GameObject.Find ("Button2").GetComponent<Button>().onClick.AddListener(delegate { Btn_Delete();});
        GameObject.Find ("Button5").GetComponent<Button>().onClick.AddListener(delegate { Btn_Clear();});

       ChatBox = UNET_SyncList_2.FindObjectOfType<UNET_SyncList_2>();
    }

    public void Btn_Add ()
    {
        if (!hasAuthority)
            return;

        print ("Btn_Add");
        Cmd_AddOnButton ();
    }

    [Command]
    public void Cmd_AddOnButton () {
        print ("Cmd_AddOnButton");
        ChatBox.myList.Add ("NUMBER 1");
        ChatBox.myList.Add ("NUMBER 2");
        ChatBox.myList.Add ("NUMBER 3");

        foreach (string _str in ChatBox.myList)
            print (_str);
    }

    public void Btn_Delete ()
    {
        if (!hasAuthority)
            return;

        print ("Btn_Delete");
        Cmd_DeleteOnButton ();
    }

    [Command]
    public void Cmd_DeleteOnButton () {
        print ("Cmd_DeleteOnButton");
        if (ChatBox.myList.Count > 0)
        {
            ChatBox.myList.RemoveAt(0);
        }

        foreach (string _str in ChatBox.myList)
            print (_str);
    }

    public void Btn_Clear ()
    {
        if (!hasAuthority)
            return;

        print ("Btn_Clear");
        Cmd_ClearOnButton ();
    }

    [Command]
    public void Cmd_ClearOnButton () {
        print ("Cmd_ClearOnButton");

        ChatBox.myList.Clear();

        foreach (string _str in ChatBox.myList)
            print (_str);
    }
}
1 Like

Whoa, epic tutorial

1 Like

There is a bug where SyncVar/SyncList hooks/callbacks will not always work if changed around a scene transition. The SyncList values will be set correctly, but callback will not be invoked.
https://fogbugz.unity3d.com/default.asp?833578_76432msnc8da1g6k

Also structs must be immutable. You should never change field values within the struct because this will not flag dirtyBits or invoke callbacks. No errors will be thrown, but it won’t work. I believe must generate a completely new struct and assign it.

Say you want to set the health 5th index to 13

INCORRECT (Will not flag dirtyBits)

SyncListStruct[5].health = 13;

CORRECT

SyncListStruct[5] = new MyStruct(13);