How to send data over the Network using Synced Variables

As of right now (07/16/2020) the only way to transfer any sort of data across the network is using “Synced Variables”. Unfortunately these variables do not tell you when they have changed. However you can setup your own logic to do exactly that. But lets start simple:

Basic Synced Var
The most basic way to handle synced vars is by just handling the incoming data in every OnDeserialization.
Lets talk about Serialization first. Serialization is the process of converting data into a streamable form.
There is 3 callbacks that are related to serlization. OnDeserialization (being the most important), OnPreSerialization and OnPostSerialization. OnDeserialization gets callled on the client when you have received network data and have deserialized it.

So when any sort of data comes in the callback OnDeserialization is called and you can check all of your synced variables and set whatever youre setting to the current state of the variable. For example this could be a boolean telling you whether an object is active or inactive. However this would be quite a waste, since you only need to update the objects state if the variable has changed…

Unprocessed Synced Var Events
If you want to know when a specific synced var has changed you will need to check its current state against its previous state OnDeserialization.
This can be done by having a local variable that just remembers what the last known synced vars status was. In C# code (see UdonSharp) this would look like this:

[UdonSynced] int sync_myInteger = 0;
int myInteger = 0;

public void OnDeserialization()
{
   if (sync_myInteger != myInteger)
   {
       myInteger = sync_myInteger;
       MyInteger_Changed();
   }
}

However be aware, that if the Owner of the GameObject that the UdonBehaviour is on changes a synced var, it will change immediately and not necessarily getting a OnDeserialization call. If that is the case you can simply call OnDeserialization after changing a synced var. (In the graph you would do this by using the SendCustomEvent node with ‘_onDeserialization’ as the input).

Now you can just use myInteger as your variable. (or sync_myInteger in this case it doesnt really matter).

Processed Synced Var Events
Now the problem with determening an “event” when a synced var has changed, is that sometimes you want to send an “event” without changing the data or parameter. My best solution is using a string that has a suffix that differentiates each “event”. So lets say you still want to stick with an integer thats a single digit say 5 for example. the first part of your string would be “5”. The other part could be a simple integer that you ToString() (However that is quite inefficient and could be made more efficient with converting the integers bits to characters). You have to increase that Integer everytime you change the variable.

However this requires that you process your synced var OnDeserialization:

[UdonSynced] string sync_myInteger = "";
string last_myInteger = "";

public void OnDeserialization()
{
   if (sync_myInteger != last_myInteger)
   {
       last_myInteger = sync_myInteger;
       myInteger = Convert.ToInt32(last_myInteger.Substring(0,1));
       MyInteger_Changed();
   }
}

I didnt show the setting part since this last method is rather complex and specific.
However I hope that I could at least help to some degree and that all the information given is correct.

Remarks
Syncing strings is quite bit-to-data inefficient especially if done poorly. VRChat limits the amount of data you can sync (in a given time) to avoid malicious use and lower server costs. That however means that you have to be careful with how much you sync. These methods can be very expensive if not used correctly.

2 Likes