Search Unity

SyncList not always syncing to clients

Discussion in 'Multiplayer' started by KatapultStudio, May 13, 2016.

  1. KatapultStudio

    KatapultStudio

    Joined:
    Dec 31, 2013
    Posts:
    6
    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.
     
    MrLucid72 likes this.
  2. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
  3. KatapultStudio

    KatapultStudio

    Joined:
    Dec 31, 2013
    Posts:
    6
    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.
     
    MrLucid72 likes this.
  4. KatapultStudio

    KatapultStudio

    Joined:
    Dec 31, 2013
    Posts:
    6
    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.
     
  5. MrLucid72

    MrLucid72

    Joined:
    Jan 12, 2016
    Posts:
    995
    Did you guys ever come up with an ideal solution?
     
  6. pKallv

    pKallv

    Joined:
    Mar 2, 2014
    Posts:
    1,191
    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.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using UnityEngine.Networking;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class UNET_SyncList_2 : NetworkBehaviour {
    8.  
    9.     //[SyncVar]
    10.     public SyncListString myList = new SyncListString();
    11.  
    12.     private GameObject chatBox;
    13.     private GameObject myText;
    14.     private int before;
    15.     private int after;
    16.  
    17.     void Start () {
    18. //        print ("Start");
    19.         chatBox.GetComponent<Text>().text += "\n";
    20.  
    21.         if (isLocalPlayer) {
    22.             Cmd_AddOnServer ();
    23.         }
    24.     }
    25.  
    26.     [Command]
    27.     public void Cmd_AddOnServer () {
    28.         myText.GetComponent<Text> ().text = "Command";
    29.         print ("Command");
    30.         myList.Add ("LETTER A");
    31.         myList.Add ("LETTER B");
    32.         myList.Add ("LETTER C");
    33.  
    34.         foreach (string _str in this.myList)
    35.             print (_str);
    36.     }
    37.  
    38.     public override void OnStartClient() {
    39.         chatBox = UNET_SyncList_2.FindObjectOfType<UNET_SyncList_2>().gameObject;
    40.         myText = GameObject.Find ("TextX");
    41.         myList.Callback += MyCallback;
    42.     }
    43.  
    44.     private void MyCallback(SyncListString.Operation op, int index) {
    45.         string idx = "";
    46.         if (myList.Count > 0)
    47.         {
    48.             idx = myList[index];
    49.         }
    50.         string str = "Op=" + op.ToString () + " Index=" + idx + " New Array Length=" + myList.Count;
    51.         chatBox.GetComponent<Text>().text += str + "\n";
    52.     }
    53.  
    54.  
    55. }
    Here is the player script:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4. using UnityEngine.UI;
    5.  
    6.  
    7. public class Player : NetworkBehaviour {
    8.     public UNET_SyncList_2 ChatBox;
    9.  
    10.     // Use this for initialization
    11.     void Start () {
    12.  
    13.     }
    14.  
    15.     // Update is called once per frame
    16.     void Update () {
    17.  
    18.     }
    19.  
    20.  
    21.     public override void OnStartClient()
    22.     {
    23.         GameObject.Find ("Button1").GetComponent<Button>().onClick.AddListener(delegate { Btn_Add();});
    24.         GameObject.Find ("Button2").GetComponent<Button>().onClick.AddListener(delegate { Btn_Delete();});
    25.         GameObject.Find ("Button5").GetComponent<Button>().onClick.AddListener(delegate { Btn_Clear();});
    26.  
    27.        ChatBox = UNET_SyncList_2.FindObjectOfType<UNET_SyncList_2>();
    28.     }
    29.  
    30.     public void Btn_Add ()
    31.     {
    32.         if (!hasAuthority)
    33.             return;
    34.  
    35.         print ("Btn_Add");
    36.         Cmd_AddOnButton ();
    37.     }
    38.  
    39.     [Command]
    40.     public void Cmd_AddOnButton () {
    41.         print ("Cmd_AddOnButton");
    42.         ChatBox.myList.Add ("NUMBER 1");
    43.         ChatBox.myList.Add ("NUMBER 2");
    44.         ChatBox.myList.Add ("NUMBER 3");
    45.  
    46.         foreach (string _str in ChatBox.myList)
    47.             print (_str);
    48.     }
    49.  
    50.     public void Btn_Delete ()
    51.     {
    52.         if (!hasAuthority)
    53.             return;
    54.  
    55.         print ("Btn_Delete");
    56.         Cmd_DeleteOnButton ();
    57.     }
    58.  
    59.     [Command]
    60.     public void Cmd_DeleteOnButton () {
    61.         print ("Cmd_DeleteOnButton");
    62.         if (ChatBox.myList.Count > 0)
    63.         {
    64.             ChatBox.myList.RemoveAt(0);
    65.         }
    66.  
    67.         foreach (string _str in ChatBox.myList)
    68.             print (_str);
    69.     }
    70.  
    71.     public void Btn_Clear ()
    72.     {
    73.         if (!hasAuthority)
    74.             return;
    75.  
    76.         print ("Btn_Clear");
    77.         Cmd_ClearOnButton ();
    78.     }
    79.  
    80.     [Command]
    81.     public void Cmd_ClearOnButton () {
    82.         print ("Cmd_ClearOnButton");
    83.  
    84.         ChatBox.myList.Clear();
    85.  
    86.         foreach (string _str in ChatBox.myList)
    87.             print (_str);
    88.     }
    89. }
     
    MrLucid72 likes this.
  7. MrLucid72

    MrLucid72

    Joined:
    Jan 12, 2016
    Posts:
    995
    Whoa, epic tutorial
     
    pKallv likes this.
  8. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    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)
    Code (csharp):
    1.  
    2. SyncListStruct[5].health = 13;
    3.  
    CORRECT
    Code (csharp):
    1.  
    2. SyncListStruct[5] = new MyStruct(13);
    3.  
     
    Last edited: Jan 3, 2017