Search Unity

networked car multiplies like rabbit

Discussion in 'Multiplayer' started by llavigne, Jan 22, 2008.

  1. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Howdy,

    I changed a portion of the network level loader so if only one level is listed, it loads this level without showing buttons. The result is multiplication of buggies... when starting a network I have one buggy, if a client connects, I get 4 etc... rabbits I say rabbits.

    Code (csharp):
    1.  
    2. function OnGUI ()
    3. {
    4.     // When network is running (server or client) then display the levels
    5.     // configured in the supportedNetworkLevels array and allow them to be loaded
    6.     // at the push of a button
    7.     if (Network.peerType != NetworkPeerType.Disconnected)
    8.         if (supportedNetworkLevels.length>1)
    9.     {
    10.         GUILayout.BeginArea(Rect(0, Screen.height - 30, Screen.width, 30));
    11.         GUILayout.BeginHorizontal();
    12.        
    13.         for (var level in supportedNetworkLevels)
    14.         {
    15.             if (GUILayout.Button(level))
    16.             {
    17.                 // Make sure no old RPC calls are buffered and then send load level command
    18.                 Network.RemoveRPCsInGroup(0);
    19.                 Network.RemoveRPCsInGroup(1);
    20.                 // Load level with incremented level prefix (for view IDs)
    21.                 networkView.RPC( "LoadLevel", RPCMode.AllBuffered, level, lastLevelPrefix + 1);
    22.             }
    23.         }
    24.         GUILayout.FlexibleSpace();
    25.         GUILayout.EndHorizontal();
    26.         GUILayout.EndArea();
    27.     }
    28.     else
    29.     if (!loaded)
    30.     {
    31.         loaded = true;
    32.         // Make sure no old RPC calls are buffered and then send load level command
    33.         Network.RemoveRPCsInGroup(0);
    34.         Network.RemoveRPCsInGroup(1);
    35.         // Load level with incremented level prefix (for view IDs)
    36.         networkView.RPC( "LoadLevel", RPCMode.AllBuffered, supportedNetworkLevels [0], lastLevelPrefix + 1);
    37.     }
    38.          
    39. }
    40.  
     
  2. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    In the original code, the RPC function "loadlevel" runs when someone pushes the button. Your appended code runs everywhere as long as there is only 1 (or less) levels, so it runs on both the client and the server, calling the RPC "loadlevel" function twice. When an RPC is called it is invoked on all clients/servers. Therefore the level is "loaded" twice (one invoked locally and one by the other network instance) and the car instantiation code runs twice, spawning 2 cars each for the server and client.
     
  3. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    So essentially one car per client is insured by the user clicking the button...

    So how would you make sure that only one buggy gets called ? I've tried isClient and the server becomes dedicated which isn't what I want, couldn't find isConnected which would allow me to test for this.
     
  4. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    Just make the server in charge of initiating a load level command, for example by doing

    Code (csharp):
    1.  
    2. if (!loaded  Network.isServer)
    3.  
    The loadlevel RPC call is buffered so all connecting clients will invoke the function and trigger their own instantiation routine.
     
  5. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    It works very well.

    If the player spawns 10 drones, each drone needs to have a network listener in it and that's it?
     
  6. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    To put it simply, if you attach a Network View to the prefab (it watches the transform by default), and instantiate with Network.Instantiate. Then yes, it will just work. The drone will appear on all clients when instantiated and be position synchronized.

    However, the real answer depends on what you are doing. Theres more to it than that. There is the RPCMode of the Network.Instantiate call, maybe you want interpolation of the transform synchronization and there is more to do if you want more than simple position synchronization. You might need to synchronize animation (see the Third Person example). Just look at how the car prefab in example and see its set up. Then there is more info in the networking manual on state synchronization, RPC functions, etc.
     
  7. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Thanks tons, this clarifies a lot... and exposed the gap between what I know and what I want to do...

    Free your mind and jump.

    Now what's the bandwidth footprint of an array of 10 GO (prefab which has a pos, rot, a script that exposes 20 x floats) vs an array of 10 (int, pos, rot, 20 x floats) ?

    Also you talk of Network.Instantiate(), is it the same as GameObject.Instantiate? I mean can I do a
    var newGO = Network.Instantiate()
    newGO.transform.parent = otherGO ?
    In term of performances is it better to use it or to concatenate all instantiation into an array of similar information and have the code recreate the hierarchy and synch up variables on the other client's end?

    I want to determine if I should first do the game solo and then change all my instantiate with network.instantiate or if I should jump right in (I'm leaning toward the latter as it appears to me networking this fairly complex structures would require a LOT of rework)

    PS: The third person example actually doesn't synch up animation for me (unless there is a tutorial I missed).
     
  8. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    You pay approximately a 4-6 byte overhead for each GO, with the view ID, timestamp, etc when doing state synchronization. So you save ca. 40 to 60 bytes per update with 10 GOs (600 B/s to 900 B/s with 15 updates per second) if you mash everything into an array yourself. So you must make up your own mind if the pain of managing every variable sent yourself + applying it to the appropriate recipient on the other end, is worth the saved bytes in traffic.

    Network.Instantiate does the same thing as an Object.Instantiate only it does it on all clients. You can do manual instantiations yourself using RPCs which is basically what Network.Instantiate does for you. There is an example of manual RPCs in the scripting reference for networkView.RPC. You could roll 10 instantiations into one such RPC if you wanted. There is a bit higher overhead in sending RPCs, approximately 7-9 bytes, except the first time they are used as the function name is sent but after that a 1 byte index. But if your are not doing several instantiations per second, it say its hardly worth the trouble.

    You will probably need to do more than just replace Instantiate with Network.Instantiate to network enable your game. There is a bit of legwork needed to set up the networking, like setting up the connection mechanism and managing players in the game. Also you need to determine what to synchronize, what functions should be RPC functions, etc. There can be a bit of changes if you decide to do server-authoritative. You could do the groundwork in single player and then try to network enable it and see how it goes.

    You might want to try redownloading the project, there was an error in there after release but it was fixed and a new project uploaded.
     
  9. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    From my own experience, I would highly recommend starting with networking as early as possible - especially if this is your first networked game in Unity. Reason is simple: There's quite a few things you might run into, and these are really "easy to fix" when you're running into them early, but can be quite painful when you run into them later...

    At the same time, however, I would recommend keeping up a single-player version because in many cases, that'll be much faster to "tweak" many of the things that need to be tweaked, or try out new features etc.

    Jashan
     
  10. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Thanks for the data profile Larus.
    What is the scope of the network viewer, it seems to instantiate on the client side as well even without calling network.instantiate (viwer attached to GO which contains the script doing the instancing).

    Jashan, you mention that quite a few things can go wrong with networking but that they are easy to fix, can you expand on this?
     
  11. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    I wouldn't necessarily say that things can "go wrong", but that you might have to change the way you're doing things when networking. For instance, if you have a look at JCsUMTRON, there's this "cut-scene" when a level starts. What I'm doing there is quite a bit of scripting which is spread across the "Game", "Teams" and "individual cycles". There's some Coroutines which involve some "waiting for things to be finished", and some camera switches, camera effects etc.

    In "single player mode", I didn't have to think of much - but when it came to the network implementation (which is fully synchronous, i.e. the cycles spawn pretty much at the same time on all clients, if there's no significant different in lag times), I had to approach this in a very different way.

    What I have right now is two completely different code paths for "network" and "single player", which is not really nice (and not really necessary). If I had started it multiplayer right away, I could have come up with something that works smoothly no matter whether it's multiplayer or not. Obviously, there's still some parts which are "network only" while others are "single player only", but doing it right from the start could have minimized that.

    Obviously, it's never too late and I could still refactor this to be more maintainable, but that consumes quite a bit of time.

    Another issue I ran into was that I have quite a few "data objects" which originally were childs of MonoBehaviour. However, I can't easily instantiate those the way I needed it for networking. So, to make networking work the way I wanted it to work, I converted those MonoBehavior-childs to simple C# classes, which do pretty much exactly what I want (that approach has other disadvantages, though, i.e. you can't directly set properties of simple C# classes in the editor).


    I guess "in a nutshell": You need to have an awareness of what to make an RPC, and what not. You can easily call an RPC directly from within your code, but - obviously - you can't call a non-RPC method over the network. Sometimes, you may want to "wait" for the clients to be "done" with something, which then involves callbacks. That way, what previously was a simple "loop" may become a rather complex distributed interaction between different machines involving stuff like "send out an RPC to everyone and then wait until all the callbacks are received"... but... what if one of the clients crashed meanwhile? ;-) I kind of love that, though ;-)

    Also, there's restrictions on the parameter types you can use on RPCs, which is also something you should best be aware of when designing your "game API", or you'll end up having a lot of "conversion methods" that do nothing but convert your "native" datatypes to datatypes you can use for networking (which, however, can also be a very good, and in some cases necessary thing to do).

    Another thing that may not be trivial: You need to somehow manage your "multiple players" in a way that works smoothly when networked. The earlier you "do this right" (i.e. compatible with networking), the less work you'll have in the long run ;-)


    On the other hand: This may also depend on the type of game you're developing. Might be that in some cases, all you need to do is change "Instantiate" to "Network.Instantiate" and add a bunch of network views, and "it just works". But then again: It's best to find that out and see it actually working as early as possible in your project ;-)

    Sunny regards,
    Jashan
     
  12. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Thanks Jashan. I think the tutorial will help a lot, I'm noticing anomalies which are related to resetting states and cold breaking server connection...

    What I am doing at the moment is take small parts of the game code and test them one by one with networking in a separate scene. I'm breaking a lot of stuff in the process but it's the only way I know to get it sturdy.

    EDIT: Larus, I downloaded the latest networking example and the third person scene still doesn't animate the other lerpz
     
  13. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280

    I messed up a bit when I uploaded the updated project zip file. It was overwritten a while later.

    I've re-uploaded the zip.
     
  14. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Thanks Larus, it's now working.
    Which line was bad and how did you fix it? (I tried to fix it myself)
     
  15. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    The TPS-Auth and ThirdPerson scenes use different player prefabs. They are the same model and such but have different controller and networking scripts attached. The TPS-Auth prefab is called NetworkPersonPlayer and is in the Authoritative server folder. It's animation component was broken.
     
  16. llavigne

    llavigne

    Joined:
    Dec 27, 2007
    Posts:
    977
    Is it more economical to pass a GameObject or a Transform as a parameter in the networkViewer.RPC() call ?