Search Unity

SyncList behaviour has been changed in 5.3.2p4

Discussion in 'Multiplayer' started by tt96, Feb 24, 2016.

  1. tt96

    tt96

    Joined:
    Feb 12, 2016
    Posts:
    7
    After latest update (738047) - Networking: SyncLists now only send updates when values change
    the behaviour of SyncList has been changed.
    Callback function is calling before internal buffer is updated now.
    I'm always getting previous value and not sure that it is correct behaviour.
     
  2. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    What is your code? (With comments, so I can read clearly.)
     
  3. tt96

    tt96

    Joined:
    Feb 12, 2016
    Posts:
    7
    I hope this example is clear enough.
    Values are always different
     
  4. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    StartCoroutine() is only run once. When it is called, it will execute from where it left off. For more info, please read Coroutines.

    Put StartCoroutine() function call in Update() and check again.
     
  5. Severos

    Severos

    Joined:
    Oct 2, 2015
    Posts:
    181
    When you put StartCoroutine in Update it'll be called on every single frame, also that function never ends, it's infinite loop with 2sec interval.
    Don't have Unity running right now, but maybe you can try removing the loop and use InvokeRepeating("Updater", 2f,2f)
     
  6. iamnotseanr

    iamnotseanr

    Joined:
    Jan 26, 2016
    Posts:
    4
    that Updater function is running on the client?? it should only run on the server
     
    Chom1czek likes this.
  7. tt96

    tt96

    Joined:
    Feb 12, 2016
    Posts:
    7
    Thanks to all for participating

    Coroutine is not a problem at all and works as expected. It was used only for decreasing amount of debug outputs.

    For this particular example I used NetworkManager as a Host ( client and server in one process), because the problem is easy reproducible. Believe me, you will get the same problem if you will split the code for client and server parts.
     
  8. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Okay, I could see where you're getting at. Bear with my handwriting, for I have a crappy, 8 years old drawing tablet.

    This is what you have at the moment:




    To fix this issue, you need to mark your variables dirty.

    Code (CSharp):
    1. IEnumerator Updater() {
    2.     while (true) {
    3.         counter++;
    4.         CmdUpdateValue(counter);
    5.         this.list.Dirty(0);   //<----  Mark it dirty prior to being called. The "0" is the index.
    6.  
    7.         Debug.Log("SyncList internal: " + list[0]);
    8.  
    9.         yield return new WaitForSeconds(2f);
    10.     }
    11. }
    And this is what happens after:



    The extra Synclist callback log message is due to "OnListChanged" callback event you have set, since it's being updated.

    That is what you need to do. Good luck.
     
  9. tt96

    tt96

    Joined:
    Feb 12, 2016
    Posts:
    7
    Thank you for your reply. As you correctly noticed I will get twice notification in this case - one incorrect, and second correct. This is not acceptable for me.

    My intention of this post was to get attention of unity developers. From my point of view this is a bug.
     
    Last edited: Feb 26, 2016
  10. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Well, by all means, report your findings to Unity via the toolbar menu, Help -> "Report a Bug..." in the editor. Let them decide what to do in your case.
     
  11. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
  12. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    Yes, the behaviour has changed and I can see the issue myself here, sorry about that, this is a regression so has high priority and I'm working towards getting the fix into next patch release. If you have a bug ID for this send it to me so I can attach it to the fix (if not I'll just create on myself). Thanks for brining this up and providing the clear repro.
     
    mischa2k likes this.
  13. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    Yeah. I finally ran into this problem myself. I had been populating my SyncLists AFTER spawning players. However, now that my database is up and running, I populate a player's inventory (SyncListStruct) before calling NetworkServer.AddPlayerForConnection(). I'm running 5.3.2f1 Personal...and the server has the correctly populated SyncListStruct upon spawning the player, but the client's SyncList is empty.
     
  14. tt96

    tt96

    Joined:
    Feb 12, 2016
    Posts:
    7
    Thank you for your response. Case 774970 was created by me.
     
  15. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    This might be another bug that I encountered too: http://forum.unity3d.com/threads/un...structs-before-addplayerforconnection.381606/
    If you use a filename that UNET doesn't like, then the synclists aren't properly synced to the clients. I noticed that when populating them after AddPlayerForConnection, they will be send to the client but only to that client, not to all the other clients around him. And if you populate them before AddPlayerForConnection then they aren't sent to any client. Unless you rename the file name, then everything works.
     
  16. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    Hmmm. Seeing the different file names you used, I don't see a pattern, so I don't think that's the actual cause.
     
  17. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Hmmmm, looks like I'm somewhat affected, even though I already made use of codes. I guess I have some hacky workarounds?
     
  18. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    Well there's only 2 ways you'll be affected (it seems like).
    1. You need to be making use of the SyncList callback.
    2. You need to populate a SyncList before the GameObject is spawned.
    And of course you need to be running 5.3.2 or higher.
     
  19. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    I am populating my SyncLists, but I used delegates to work around this. So, I guess I'm not entirely affected. Thanks though.
     
  20. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    So you're doing:
    1. Instantiate()
    2. SyncList.Add()
    3. NetworkServer.Spawn()
    In that order?
     
  21. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    1. Have 5.3.3f1.
    2. Instantiate()
    3. NetworkServer.Spawn()
    4. Call RPC1
      1. In RPC1, call CMD1
      2. in CMD1, SyncList.Add() instantiated object and attach delegate callbacks here.
     
  22. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    Nope. You're not doing it in a way that you'd encounter the bug. I know you can't, since 4 is a CMD, but if you could...if you switched 4 and 3 around you'd notice the bug. There's nothing wrong with the way you're doing it, and it's not a workaround...it's just not the method of implementation that triggers the bug.
     
  23. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Ah ok. I guess the order does matter. I had this hunch since last year, yet I didn't realize it.
     
  24. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    I read through the Improvement/Fixes lists for 5.3.4 and I don't recall seeing it but I figured I'd ask. This still hasn't been fixed yet, has it?

    EDIT : Nevermind. I checked the Issue Tracker page and it says it's scheduled to be fixed in 5.3.5.
     
  25. Jesse_Pixelsmith

    Jesse_Pixelsmith

    Joined:
    Nov 22, 2009
    Posts:
    296
    Broken in 5.4? Will 5.3.5 fix get rolled into 5.4 before it gets out of beta?
     
  26. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    Yep, still broken in b18.
     
  27. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    Not broken for me anymore.
     
  28. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    @DRRosen3, which version are you running? The fix is indeed supposed to have been added to 5.3.5.
     
  29. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
  30. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    TLDR;
    There is not really a SyncList bug, but an overall SyncList callback design flaw. SyncVar and SyncList callbacks need to and provide both OldVal and NewVal information to be usable.


    Currently [SyncVar] callback's provide information about the oldVal and newVal, but SyncLists do not. This is a design flaw that will make SyncLists unusable in many cases.

    This was the bug before that showed inconsistent behaviors on SyncList callbacks. I haven' tested recently.
    http://forum.unity3d.com/threads/sy...set-does-not-have-access-to-new-value.339029/

    Regarding SyncList callbacks there are 4 general options.
    1: Callback called before value change. The issue: you don't have access to the newVal.
    2: Callback called after value change. The issue: You don't have access to the oldVal. (currently what's intended for SyncList)
    3: Callback called before value change. Provide new value information in callback. (currently what SyncVar do)
    4: Callback called after value change. Provide old value information in callback. (What I propose)

    Currently option#2 is what Unity does, but the problem is there is no access to the old value which often will be important depending on how the SyncList is used. I propose that Unity switch to callback to option #4 for SyncLists.

    Currently:
    Callback(SyncList<T>.Operation op, int index)

    Proposed:
    Callback(SyncList<T>.Operation op, int index, T oldValue)

    On a related note [SyncVar] callback's do option #3. They are called before value change and provide you information on the newVal in the callback. This allows access to both oldVal and newVal data, unlike SyncLists.

    Let me give you an example of why the SyncList callback information is insufficient in the current form. I am attempting to use SyncList to manage On/Off visual effects (swords, helmets, status effects like burn/frozen). The advantage of SyncLists is that late-connecting players will have up-to-date information on other players equipment and status effects.


    So lets say every visual effect in the game is assigned an integer.
    100 = steel helm
    101 = viking helm
    102 = fire sword
    103 = bronze sword
    104 = frozen
    105 = burning
    106 = poisoned

    So if the syncList contains 4 values of 100, 102, 105, 106 then he's wearing a steel helm, fire sword, and burning while poisoned. Sounds easy right? But it doesn't work due to lack of information in the SyncList callback.

    ***ADD: Works Fine***
    If callback option #2 is used then I can add an effect. Lets say I add a frozen effect.
    SyncList.Add(104) //this adds frozen.

    Then the Add callback gets
    Callback(SyncListInt.Operation op, int index)
    //op = OP_Add
    //index = 4 //index of 4 because list already contains 4 items
    I can access syncListInt[4] and it returns 104... this allows me to add the frozen prefab associated with index 104

    ***REMOVE DOES NOT WORK BECAUSE THERE IS INSUFFICIENT CALLBACK INFORMATION***
    SyncList.Remove(104) //this removes frozen
    Callback(SyncListInt.Operation op, int index)
    //op = OP_Remove
    //index = 4

    I have no information about what was removed because the oldVal is not provided in the callback!!! I cannot access syncListInt[4] because that would generate OutOfIndex errors because the value has already been removed on the list.

    I hope this makes sense, I tried to explain the best I could.


    I respectfully request Unity make a pass at all callbacks for SyncList and make them consistent and provide BOTH oldVal and newVal data just like [SyncVar] callback's do. In their current form SyncList is not usable because no information about the oldVal that was removed (or changed) is provided.
     
    Last edited: Jun 9, 2016
    Marble likes this.
  31. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    Nice. Even though it's usually a vain exercise, perhaps it would be worth posting this on the feedback site so that we could vote for it. I agree with your issue and your solution. The SyncList Op.Remove callback is functionally useless for the reason that you mentioned.
     
    Zullar likes this.
  32. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    Marble likes this.
  33. Zullar

    Zullar

    Joined:
    May 21, 2013
    Posts:
    651
    OK so I think I'll summarize the issues.
    1: There are currently SyncList bugs that making SyncList callback's timing inconsistent. Sometimes it's before value change, and sometimes it's after value change.
    2: Even after the "bug" is fixed and all SyncList callbacks occur after value change, the SyncList callback still does not provide enough information in some instances where oldVal is needed.
    3: SyncList/SyncVar callbacks are not called on Start (intentionally). So late connecting players must manually look at the SyncList and extract information about what has been added without using Callbacks. This forces quite a bit of additional work and leaves room for error.

    Unity is only fixing issue #1, not #2 or #3.
    I'd propose to fix issue #2 the oldVal should be provided in the callback similar to SyncVar's callback.
    I'd propose to fix issue #3 have a bool flag on the SyncList constructor that can toggle on/off whether the Callbacks are called on Start or not. Also do this for SyncVar.

    ***Workaround***

    As a workaround to issue #2 and #3 you can keep a copy List<int> see what was removed/changed. During Start() or anytime the SyncList callback is called then compare the lists and learn what has been added/removed/changed. It's a bit of an ugly bandaid, but seems to be working... and seems like the cleanest way to work around both issue #2 and #3.

    Ping me if anybody wants workaround code.
     
    Last edited: Jun 10, 2016
  34. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    This bug was reintroduced for the second time now in 5.4.
    It does seem to be fixed in 5.5.0b2.

    Any chance for a fix in 5.4 again? Or can you add some kind of test to make sure it stops being reintroduced over and over again?