Search Unity

State Synchronization

Discussion in 'Multiplayer' started by Pia, Feb 16, 2009.

  1. Pia

    Pia

    Joined:
    Feb 16, 2009
    Posts:
    78
    How does State Synchronization exactly work? I kept looking at the Multiplayer example source code, but i really don't get the whole picture.

    Is State Synchronization a completly automated mechanism? Lets assume i got 2 cubes in a multiplayer enviroment, each one controlled by a different player. Now i want to synch the positions of these cubes among the players through an authorative server. How would i do that? After reading the documentation i thought it would be enough to assign a NetworkView to each GameObject i want to synchronize and thats it. But that didnt work.

    The examples and the documentation contain code like:

    Code (csharp):
    1. function OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo) {
    2.     var horizontalInput : float = 0.0;
    3.     if (stream.isWriting) {
    4.         // Sending
    5.         horizontalInput = Input.GetAxis ("Horizontal");
    6.         stream.Serialize (horizontalInput);
    7.     } else {
    8.  
    9.         // Receiving
    10.         stream.Serialize (horizontalInput);
    11.         // ... do something meaningful with the received variable
    12.     }
    13. }
    14.  
    How does the server know that the float value sent is the horizontal input? And where (in the multiplayer example) is the code which takes the incoming packets and reacts accordingly?

    I would be very grateful If someone could explain State Synchronization with a very simple example.
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    OnSerialize - write is called at the fixed network message rate you have set.
    If you set the network view to send only differences, this thought will normally not cause network traffic

    OnSerialize - read is called whenever you receive a state change from the "other end"


    Neither side cares about what data is stored in the message. Its the receive code that will handle that as you must readout the values in the same order in which you wrote it in.

    The main reason its not fully obvious what the read does is JavaScript.
    It hides an elemental part there:

    If you check out the serialize in C#, you will see that it is stream.Serialize(out somevariable), which means that the content of the variable actually can be altered to return a value.
    In the "isWriting" case, it will read the value in the variable and write it to the stream.
    But in the reading case it will actually read sizeof(somevariable) from the stream (so the content of the variable) and store that in your somevariable
     
  3. Pia

    Pia

    Joined:
    Feb 16, 2009
    Posts:
    78
    I understand that part. But how do I connect two entities? How can I tell which object will receive the data I sent?
     
  4. Bugfoot

    Bugfoot

    Joined:
    Jan 9, 2009
    Posts:
    533
    NetworkViews are what connect a server-side object to its client-side version. Picture it as a one-way tunnel in which the synchronized data transits. The synchronized data is whatever you add in your OnSerializeNetworkView().

    Now in order to "make it happen", you have to connect the networkView on the server-side to the corresponding networkView on the client-side. There are two ways to do this:
    1. Automatic: use Network.Instantiate() to create your networked object. This will automatically take care of allocating a new NetworkViewID and assign it to the networkView on both sides
    2. Manual: you have to allocate a new networkViewID and assign it to the networkView on both sides of the wire. Typically, you'd provide the networkViewID to the remote client via an RPC call. I think you might also have to setup the NetworkView ownership which tells the engine which "side" is the authoritative one. Typically, the NetworkView owner would be the server-side in an authoritative server model.

    Once the two NetworkViews are connected together via this NetworkViewID, the engine will automatically detect when the your serialized properties change on the side of the NetworkView's owner, and propagate them to the NetworkView on the other end of the wire.

    Also to answer your question on serialization: Unity doesn't "know" that it's the horizontal property. What matters is that the order in which your serialize your values mirrors the order in which your deserialize them. But since Unity smartly uses the same method to serialize and deserialize the objects (stream.Serialize), you don't have to worry about this.

    If the stream is in write mode (=you are sending data on the server side), then the value you pass in will be appended to the stream. If the stream is in read mode (=you are receiving data on the client side) then the value will be extracted from the stream and copied to the parameter you pass in by reference. Since Unity knows how many bits each supported data type is (int=32 bits, boolean=1 bit, etc) it can read or write the correct amount of bits from the stream by checking what type of data you are passing as parameter to the stream.Serialize() method.

    Does it make more sense?
     
    Gixtox likes this.
  5. Pia

    Pia

    Joined:
    Feb 16, 2009
    Posts:
    78
    Yes it does, thank you!

    One last question: What happens if I don't override the OnSerializeNetworkView(...) method? Is the position/rotation automatically synchronized or does that mean that no data at all is exchanged?
     
  6. Bugfoot

    Bugfoot

    Joined:
    Jan 9, 2009
    Posts:
    533
    Yeah this part was a bit obscure for me at first as well.

    Basically, one NetworkView can only observe one component at a time. NetworkViews come with built-in support for some of the most common standard components, such as Transform and Rigidbody, so you can add a NetworkView to your object to monitor either of these without adding any special code. However, if you have custom components (scripts you wrote) attached to your objects and you want to network some of their data, you will need to add a NetworkView that observes whichever script whose data you want to synchronize.

    So for instance, if you have a HealthComponent script (with a health property you want to synchronize) and an AmmoComponent script attached to your player object (with a ammoCount property that you want to synchronize), then you will need to add two NetworkViews to your player object: one will be set to observe the HealthComponent, and the other one will be set to observe the AmmoComponent. Each script will also have to override the OnNetworkSerialize() method to serialize the property they want to have synchronized over the network (health for HealthComponent, and ammoCount for AmmoCompoent).

    Now, if in addition of this, if you also want the player's position rotation to be synchronized with the server you will have to add a 3rd NetworkView to your player object that observes its Transform component. Here you don't need to provide any extra code, as the Transform is one of these standard component with built-in network support.

    However, this might become a bit messy if you have a lot of custom scripts that require network synchronization. I found that an easier approach was to have a custom script dedicated to networking (I called it PlayerNetwork) in which I get/set the properties from the other player components that I want to network and serialize them in OnSerializeNetworkView(), so everything is in one place. I find this more convenient, as it decouples the network code from the rest of the game logic.

    Hope this helps.
     
    Gixtox likes this.
  7. Manakel

    Manakel

    Joined:
    Mar 19, 2009
    Posts:
    6
    Hello,

    Is there a limit on how many NetworkViews can watch the same script?

    Can i have a networkview that watch the health property of my PlayerStatus with a very high update rate and another networkview that watch the armor name property of my PlayerStatus with a low update rate?

    (provided each view only serialize the right property of course)
     
  8. zelk

    zelk

    Joined:
    Mar 13, 2009
    Posts:
    246
    Very informative thread! Thank you!

    I am new to unity networking but I know a little bit about TCP/IP and UDP communication and the overhead for each package is bigger than the contents of the package.

    Say you have one packet that is sent very often with the health only... you will send an UDP package which will consume a whole lot more traffic than just that single int, since the communication layers add so much...

    So if you need to send some stuff often and some stuff not so often, I belive you will actually save bandwidth by sending all at once (that is, often). However, if you have large quantities of data (like a few hundred or thousand bytes) you shold use your approach and there, I do not have an answer to wether that works or not.
     
  9. victoryismine06

    victoryismine06

    Joined:
    Apr 3, 2009
    Posts:
    21
    I second that! Kudos to Spk and dreamora for all the information. I've been tryin to figure this out for a while now and after reading this, I fixed sooo many issues. Thanks! But just to be clear OnNetworkSerialize() only sync's between that specific client and the server, not automatically between all connected clients, right?
     
  10. DrHotbunz

    DrHotbunz

    Joined:
    Feb 14, 2009
    Posts:
    315
    And we are still liking this post in July!