Search Unity

Bringing newly Client Player "up to Speed"

Discussion in 'Multiplayer' started by Der Dude, May 11, 2008.

  1. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    Hello everyone,

    I've run into a little problem I hope you can help me with.

    After you open a server in our game, you can wait in a Lobby for other Players. You can however also start the game at any time.
    When a new client connects and the server already started the game this issue occurs.

    Everything seems to be intialized correctly. The NetworkViewIDs are all allocated appropriatley.

    There are no errors on the server.

    The errors on the client all look like this:
    Code (csharp):
    1. Received state upate for view ID SceneID: XX Level Prefix: 2 but no initial state has ever been sent. Ignoring message.
    I noticed that all the affected NetworkViews were those set to Unreliable synchronization. All other NetworkViews work fine (synchronization set to Off). Even those instantiated in that level. No matter if allocated or Scene ID.

    This does not occur when the Client connects while the server is still in the Lobby.

    I hope I could describe my problem clear enough for you guys to help me.

    Cheers!

    Edit:

    I did some further testing.
    NetworkViews that are instantiated while the Client is connected that use unreliable synchronization work.

    After disabling all unreliably synchronizing NetworkViews in the scene, the scene loads fine.

    So the problem lies with NetworkViews that start their Synchronization before a player connects.
     
  2. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Hey Dude ;-)

    unfortunately, as far as I know, this kind of thing (starting a game and letting others join in later) is not really supported by the "more advanced" networking stuff in Unity (in particular state synchronisation). I truly hope that this will be fixed in one of the next releases as that means you'll have to implement a lot of things on a lower level that Unity actually provides - but it just provides those things in a too specific manner.

    I've had quite a few similar problems with TRaceON, where people can join in at any time, and also where different game sessions can take place in the same level, or even in different levels all happening simultanuously on the server. While there might be another solution to your problem (that I'm just not aware of), that is something that totally won't work with Unity's network synchronization.

    Luckily, my game logic doesn't really need the network synchronization that Unity offers. So, all I'm doing is sending RPCs, and so I was able to create my own layer that sends the RPCs only to those clients to which those RPCs are relevant. I'm also not using any buffered RPCs (using buffered RPCs might actually be the solution to your problem, if there is a solution).

    The only problem that I'm still having are occasional "Network.Destroy()" commands that the server sends out (I don't even know why it does that - I'm not using Network.Instantiate() in any of my code, let alone Network.Destroy()).

    Maybe I'm missing something, but as far as I understand things, if you want to do what you're doing (letting people join in later), it may not work smoothly. On the other hand, it may also be that there's an approach to that which works more smoothly (I don't have that much experience with using network synchronization).

    Sunny regards,
    Jashan
     
  3. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    Thanks for your reply!

    Using buffered RPCs won't help I think. I'm trying to avoid them anyway, as I can't be sure if an RPC-message is going to be relevant for a newly connected Player or not.

    I've got an idea though. What if all the affected NetworkViews get destroyed and instantiated again, whenever a new player joins? Then the state-synchronization would start all over again, sending the "initial state" even to the newly connected player.

    I'll try it in the next couple of days and post my results here.
     
  4. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    I've got it to work now, I think. Basically you bring the player "up to speed" in 4 steps.

    1. Client loads current level. Client notifies server when done.

    2. Server tells Client all the changes that happened to the level (player spawns, destroyed objects, etc).

    3. Client tells all other players to reinitialize their synchronizing NetworkViews. It is not necessary to reinitialize NetworkViews which are off. And every connected Player only needs to reinitialize the NetworkViews he owns.
    By reinitializing I mean Destroying the NetworkView after saving the ViewID and then Adding it again with the old ID.

    4. Client spawns his objects.

    It is essential, that step 1 is complete before step 2 commences. Also step 2 has to be finished before step 3. I believe that step 3 and 4 can happen simultaenously, but I just do them after another.

    It seems to be working fine. However I will do more intensive testing and post here, if you guys are interested.
     
  5. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Would be very interested in knowing if this solution works.
     
  6. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    An error in a script I wrote threw me off...

    This works. I changed step 3 a bit though.

    The new Client tells the server to reinitlaize ALL synchronizing NetworkViews. Then the server tells all old Clients to reinitlaize only their synchronizing NetworkViews.
     
  7. Sync1B

    Sync1B

    Joined:
    Sep 13, 2005
    Posts:
    561
    I dont understand the problem fully. I am having the same issue, every thing is instantiated properly and network views are set. But it says the initial state is not received. Is there a way to manually set the initial state? I would use network instantiate but that happens on all clients, which is not what i want. Destroying the network views and recreating them seems like a unnecessary hassle. Any help with this issue?

    Bill
     
  8. Sync1B

    Sync1B

    Joined:
    Sep 13, 2005
    Posts:
    561
    Another thought: I am using unreliable network views. I thought the idea with reliable/delta was that it only sent the difference. If I was using reliable I could understand it not receiving a initial state. How ever I am using unreliable, my assumption was that unreliable send the entire state, not the delta. Whats the deal?

    Bill
     
  9. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    To be honest, I don't understand either. The same question ran through my head.

    I just use my own UDP-Network Layer I built before Unitys came along for State Synchronization. I find problems like these frustrating, because you have no insight into the inner workings.

    RPCs are great. For everything else I use my own UDP system.

    I don't mean to put down Unitys Networking system. It is great to get into the field of Networked Games.
     
  10. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    The very first network update sent to a newly connected client is always the initial state. The client makes sure the initial state has been received when receiving network updates or else inconsistencies might occur. However, this is only important when using reliable diff compressed. If you get these errors when using unreliable state synchronization it might be a bug because the initial state doesn't really matter here (all state updates are full updates and thus equal to an initial state). Please file a bug report in that case, attaching a project with the steps needed to reproduce. Starting a server and having clients join later is fully supported. You could try setting the state synchronization method to reliable diff compressed instead of unreliable and see if that doesn't remove the initial state errors.

    Also, if you are filtering bandwidth by disabling network groups (SetReceivingEnabled and such) then that might be a factor.
     
  11. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    I block sending on the server to avoid sending updates to clients, who havn't instantiated the appropriate objects yet.

    So should I just allow sending on the server, while the new Client connects, ignoring the "Network View ID not found during lookup" messages?

    By the way, I only use Unrealiable state Synchronization.
     
  12. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    So what do you suggest would be the best practice?

    My plan was to group the NetworkViews into instantiating (1) and action (0) NetworkViews.

    While a player has not been brough "up to speed" by group 1, receiving group 0 is disabled on the client. However, when group 0 is reenabled, will the initial state update be there? Or will it be sent again?
     
  13. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    I haven't gotten around to sending a bug report, but for now I just wanted to post the code I use to reset the NetworkViews. This function is called on all machines once a new player is connected. This requires the new player to have instantiated all new (meaning not in the scene by default) Objects and players.

    players is an array of Components, which represent a player. As for now these are the only NetworkViews belonging to a player.

    Code (csharp):
    1.  
    2.         [RPC]
    3.     public void ResetLocalNetView()
    4.     {
    5.         //We are the server, reset all NetworkViews
    6.         if( Network.isServer )
    7.         {
    8.             //Iterate through all potential players
    9.             for( int i = 0; i < players.Length; i ++ )
    10.             {
    11.                 //if the player exists -
    12.                 if( players[ i ] )
    13.                 {
    14.                     // - create a copy of the existing network View -
    15.                     NetworkView newNetView = (NetworkView) players[ i ].gameObject.AddComponent( typeof( NetworkView ) );
    16.                     newNetView.observed = players[ i ].networkView.observed;
    17.                     newNetView.stateSynchronization = players[ i ].networkView.stateSynchronization;
    18.                     newNetView.viewID = players[ i ].networkView.viewID;
    19.                    
    20.                     // - and destroy it
    21.                     Destroy( players[ i ].networkView );
    22.                 }
    23.             }  
    24.         }
    25.         // We are the Client. Only reset the local Players View
    26.         else
    27.         {
    28.             //if the player exists -
    29.             if( players[ GameController.localPlayer.playerNum ] )
    30.             {
    31.                 // - create a copy of the existing network View -
    32.                 NetworkView newNetView = (NetworkView) players[GameController.localPlayer.playerNum].gameObject.AddComponent( typeof( NetworkView ) );
    33.                 newNetView.observed = players[ GameController.localPlayer.playerNum ].networkView.observed;
    34.                 newNetView.stateSynchronization = players[ GameController.localPlayer.playerNum ].networkView.stateSynchronization;
    35.                 newNetView.viewID = players[ GameController.localPlayer.playerNum ].networkView.viewID;
    36.                
    37.                 // - and destroy it
    38.                 Destroy( players[ GameController.localPlayer.playerNum ].networkView );
    39.             }
    40.         }
    41.     }
     
  14. MikeHergaarden

    MikeHergaarden

    Joined:
    Mar 9, 2008
    Posts:
    1,027
    Grr this unity-networking-bug cost me a day. Big thanks to the above posters! It's 100% a unity bug.

    This is my javascript I'm RPCing on all my players to reset the networkviewID's..pretty much copy&paste from the above post.

    Code (csharp):
    1. @RPC
    2. function ResetNetworkViews(){
    3.     for (var playerInstance : PlayerNode in playerInfo) {
    4.         if(Network.player==playerInstance.player || Network.isServer){
    5.             var newNetView : NetworkView = playerInstance.playerTransform.gameObject.AddComponent( typeof( NetworkView ) );
    6.             newNetView.observed = playerInstance.playerTransform.networkView.observed;
    7.             newNetView.stateSynchronization = playerInstance.playerTransform.networkView.stateSynchronization;
    8.             newNetView.viewID = playerInstance.playerTransform.networkView.viewID;
    9.             Destroy( playerInstance.playerTransform.networkView );
    10.             Debug.Log("Player "+playerInstance.player+" has readded netviewID:  "+playerInstance.playerTransform.networkView.viewID);
    11.         }
    12.     }  
    13. }

    I have posted yet another networking bug report..I really wish the networking bugs get some love&attention. Besides that, the unity networking is great.


    By a teammember:
     
  15. Cellwind

    Cellwind

    Joined:
    Sep 21, 2009
    Posts:
    5
    These steps didn't work for me until I used DestroyImmediate like so:

    Code (csharp):
    1.  
    2.     public void RecreateNetworkView(NetworkViewID viewId)
    3.     {
    4.         if(networkView != null)
    5.         {
    6.             DestroyImmediate(networkView);
    7.         }
    8.  
    9.         NetworkView view = gameObject.AddComponent<NetworkView>();
    10.         view.viewID = viewId;
    11.         view.observed = this;
    12.         view.stateSynchronization = NetworkStateSynchronization.ReliableDeltaCompressed;
    13.     }
    14.  
    I'm also not distinguishing between client or server, owner or not, when doing the reset operation as you can see.

    The steps I follow are as follows:
    1. New client joins the session.
    2. The server reacts by sending level info to the client.
    3. The client receives the level info and loads the current level, sending a response back to the server when done.
    4. When the server hears back from the client that it is done loading the level, it broadcasts a message to all peers in the session (including itself) tell them to "initiate" a reset operation.
    5. When a peer receives notification to "initiate" a reset operation, it broadcasts a message to all peers in the session (including itself) to do a reset for a particular GameObject, identified by some number. Here's the code:


    Code (csharp):
    1.  
    2.     static void OnAvatarReset(NetworkMessage message)
    3.     {
    4.         if(NetworkManager.IsConnected)
    5.         {
    6.             // Reset any locally controlled avatars
    7.             //
    8.             foreach(Avatar avatar in _Avatars.Values)
    9.             {                
    10.                 if(avatar.player != null  avatar.player.IsLocal)
    11.                 {                    
    12.                     Game.NetworkView.RPC("ResetNetworkView", RPCMode.All, avatar.Index, avatar.networkView.viewID);
    13.                 }
    14.             }
    15.         }        
    16.     }
    17.  
    18.  
    The current view id is used during this broadcast.

    6. When a peer receives this message, it executes the code pasted at the beginning of the post on the GameObject referenced in the RPC message (avatar.Index).

    The DestroyImmediate was the big problem for me. After changing to it from Destroy, and moving it above the recreation stuff, it worked. [/code]
     
  16. Ethan

    Ethan

    Joined:
    Jan 2, 2008
    Posts:
    501
    Anyone knows if this bug will be fixed in Unity 2.6?

    I didnt work with the allocateViewId stuff till now, but i wanted to give it a try, because Network.Instantiate on clients doesnt seem to be reliable enough.
     
  17. MikeHergaarden

    MikeHergaarden

    Joined:
    Mar 9, 2008
    Posts:
    1,027
    I'm currently lobbying for some networking features and bugfixes, and Unity is all ears. This is one of the +/-13 item's I've discussed, could anyone hook me up with a example project where this bug pops up?

    We used to have this bug very badly in our project "Verdun online", but I just commented our "ResetNetworkView" functions and the game still runs. Either the bug is gone..or we have changed some things that have made the bug disappear (for now). So; is the bug still around?
     
  18. Cellwind

    Cellwind

    Joined:
    Sep 21, 2009
    Posts:
    5
    Yes it is most definitely still around. You can repro the behaviour by having a client join a game in progress where objects use the 'delta compression' scheme.
     
  19. Noname

    Noname

    Joined:
    Nov 9, 2009
    Posts:
    7
  20. J_P_

    J_P_

    Joined:
    Jan 9, 2010
    Posts:
    1,027
    Is this still necessary in Unity3?
     
  21. zhuangfengzi

    zhuangfengzi

    Joined:
    Feb 7, 2010
    Posts:
    11
    @RPC
    function ResetNetworkViews(){
    for (var playerInstance : PlayerNode in playerInfo) {
    if(Network.player==playerInstance.player || Network.isServer){
    var newNetView : NetworkView = playerInstance.playerTransform.gameObject.AddComponent( typeof( NetworkView ) );
    newNetView.observed = playerInstance.playerTransform.networkView.observed;
    newNetView.stateSynchronization = playerInstance.playerTransform.networkView.stateSynchronization;
    newNetView.viewID = playerInstance.playerTransform.networkView.viewID;
    Destroy( playerInstance.playerTransform.networkView );
    Debug.Log("Player "+playerInstance.player+" has readded netviewID: "+playerInstance.playerTransform.networkView.viewID);
    }
    }
    }


    Where is the PlayerNode?

    Thank you for your help!
     
  22. phil_ivey

    phil_ivey

    Joined:
    Dec 3, 2010
    Posts:
    31
    still didnt fixed this error too bad
     
  23. Sollare

    Sollare

    Joined:
    Feb 14, 2012
    Posts:
    6
    ROFL when they will fix this?! xD 4 years passed
     
  24. jacky long

    jacky long

    Joined:
    Feb 27, 2012
    Posts:
    2

    你搞定了吗?
     
  25. etoreo

    etoreo

    Joined:
    Apr 11, 2012
    Posts:
    104
    I just ran into this myself. Such a bad bug. Can UNTIY Support commented on this and just give us a status at the very least?
     
  26. nventimiglia

    nventimiglia

    Joined:
    Sep 20, 2011
    Posts:
    153
    Using Unity 4. Ive just run into this exact problem - Network instances are not buffered if they use serialization.

    I really don't want to re instantiate everything whenever someone joins. This is insane.
     
  27. Caedicus

    Caedicus

    Joined:
    Nov 28, 2011
    Posts:
    23
    I also m dealing with this issue now. Didn't realize that I Network Views couldn't send initial states until I got very deep into my network code. This is very disappointing. :( Doesn't seem like it would be that hard to implement some sort of ReSendInitialState method to the network view.
     
  28. coffiarts

    coffiarts

    Joined:
    Jan 7, 2013
    Posts:
    33
    Having the same problems, using Unity 4.1.2f1.
    Strange to read this thread from start to end. Seems to be a long known issue, but but no solution in sight?
     
  29. gooncorp

    gooncorp

    Joined:
    Dec 30, 2011
    Posts:
    131
    i tend to think that join in progress was an issue for many video games for years for good/this reason. i am by no means a network expert, but i write all my own network code. i have never relied on unity to just figure things out automagically, and if i learned anything about networking it is this; it takes a bit of creativity and work.

    i am working on this issue now in my fps game "backspace", and will let you guys know what i come up with. thanks for all who have contributed to this thread.

    espen
     
  30. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    There are some ways to resolve this issue.
    If the server is auth, you can change the client value's on the server for .1 seconds and then change them back to the original value.
    Then the value will get updated on all clients since it has changed.
    Example code how update an int:

    Code (csharp):
    1.  
    2. IEnumerator OnPlayerConnected(NetworkPlayer np)
    3. {
    4.         int tmp = currentColor;
    5.         currentColor = 0;
    6.         yield return new WaitForSeconds(0.1f);
    7.         currentColor = tmp;
    8. }
    9.  
     
  31. gooncorp

    gooncorp

    Joined:
    Dec 30, 2011
    Posts:
    131

    thanks a lot man that helped
     
  32. remigillig

    remigillig

    Joined:
    Jul 4, 2013
    Posts:
    9
    Using 4.2.1f4 I found an issue which sets the Scale to 0 if you're using custom serialization, I added transform.localScale = Vector3.one in Start() to fix it, maybe you're running into this one.