#udon-networking
1 messages · Page 33 of 1
and when you start it playing, do a sendcustomeventdelayed by the audioclip length to turn that bool back off
and then instead of transmitting it by a network event, you would check the bool in ondeserialization. That's the event that happens when you receive synced data
however if this is not an urgent project, you could also use the open beta to get access to onvariablechanged, which simplifies that use case a bit
what does get length output as?
it's just the time in seconds
Ah, alright
just wanted to make sure I was using the right customeventdelayed
I assume the sendcustomeventdelayed is attached to the interact?
yes, but more than just there. What happens if someone clicks and then leaves? You need to make sure that everyone does the sendcustomeventdelayed, but then when it happens you just check if you're the owner before setting it to false
If someone clicks and leaves it just plays for the rest of the people there
which is how it would work on the original TF2 map
yes, but it would never turn the synced bool off. If someone joins later, they would be told that it's currently playing when it's not
it's not in the search menu, you get it by dragging the variable into the graph and holding ctrl
How's this so far? not finished yet but this is what I've done
I know I still need to get rid of the network event, just wanted to get the main part sorted out first
sendcustomeventdelayedseconds is not a "wait" function, so it doesn't interrupt the flow. Instead you give it a completely different function and that function will be called after the duration specified
so, what needs moving?
well right now you're just blindly toggling playing, I don't think that's what you want
you need to explicitly set it to true or false
in the place it is currently, you would set it to true
and then in the event that gets called by sendcustomeventdelayedseconds, you would set it to false
I looked it up and people were using unary negation, didn't know there was a specific node to enable and disable
unary negation would be used for a mirror toggle. It just flips the bool from true to false or from false to true
what's the node I should be using then?
the hard part about googling udon is that it comes up with really old versions
you don't need a node, set variable comes with a toggle
all that's there is a drop box and an input
so the order should be set playing, customeventdelay, and then another set playing?
no, customeventdelayed does not interrupt the flow. Everything that follows the white line happens immediately
you need to make a new custom event and then have it call that
Alright, this is what I've got so far. How would I set up the OnDeserialization part?
instead of sending a custom network event on interact, you need to first setowner to the local player, then call PlayJukebox, and then call requestserialization
you can also move the set playing true to the interact, after setting owner
also make sure that playing is a synced variable and the udonbehaviour is set to manual sync instead of continuous
Aren't I getting rid of PlayJukebox though?
not the event itself, just the network event
it's good to keep that separate from the interact because you need everyone to call playjukebox whether they received it from the interact or from the ondeserialization
so you'll also need to ondeserialization > if playing > sendcustomevent playjukebox
also in stopplaying, you'll need to check if you're the owner and also requestserialization in order to sync in manual mode
just to be clear, where does the first requestserialization go?
the one for PlayJukebox
anywhere in the interact
after setowner
requestserialization is how you say "I have new data that I want to send to other people"
and then you call PlayJukebox after set playing?
yeah
interact happens for the person clicking it, ondeserialization happens for everyone else
so you need to make sure both call playjukebox
Does this look good for the interact?
yep
the serialization doesn't happen immediately (thus it's just a request) so that's why it doesn't matter where it is. But it may make more sense logically to happen after setting the bool to true
like "you take ownership, you set the value, and then you sync the data"
just to be more readable and easier to understand what's actually happening
I'm confused where this ondeserialization goes, everything I've done so far seems to have a place already
ondeserialization is what happens when everybody else receives the synced data
So it'd be attached to another set playing, and a sendcustomeventdelayed
you already have sendcustomeventdelayed in the playjukebox, so that's not necessary
ah, right
so just call playjukebox
no, set playing is a synced variable so in ondeserialization you are receiving that, not setting it
you need to plug playing into a branch, and only call playjukebox if it's true
Like this
yeah
This is stopplaying by the way
you need to plug the localplayer into the isowner
and also when you modify a synced variable, you need to call requestserialization
that's pretty much it. The only other thing I can think of is that you probably want to block the interact while it's playing. To do that you can just set DisableInteractive true in playjukebox and then set DisableInteractive false in stopplaying before the branch
yep
I assume disableinteractive would only apply to the button it's applied to, and not the other buttons with the script
well this script is the one that has interact, so that's the one you want to disableinteractive
the TF2 map makes it so you can't change the song at all until the current song is done, but considering the effort to get this far I think I can ignore that part
no it's really easy
to disable interaction for all buttons?
oh hm it might be more annoying in graphs because udonbehaviour array isn't a thing
oh wait actually I think it will cast dynamically, one second
there, do this
oh wait wrong node, getcomponentsinchildren not just getcomponents
udonbehaviour array doesn't work
this will set disableinteractive on all the children of the "parent" object
if anything it's simpler than udonbehaviour array since getcomponents doesn't give you the proper type it just gives you a component array
I'm actually surprised, this is a lot less messy than I thought it would be. Casting like this is annoying in U#
it would probably be cleanest to make this a separate event entirely
and have one version for enabling, another for disabling
and you would disable interactive in playjukebox and enable interactive in stopplaying
it's not something you modify in graphs, you do that in the udonbehaviour component
I think I've missed something somewhere
I tested with 2 instances (with the go button so I can delay the other joining)
and it doesn't seem to do anything
This is everything
jukeboxsource and clip shouldn't be synced. You can't sync references like that
I don't think that would necessarily break things though. Are you sure those are both plugged in on the inspector?
Yeah, all of them are
I'm definitely using the thing to disable all buttons by the way, you can disable all 5 buttons one at a time
can you check the log to see if anything is crashing? You can find it in appdata/locallow/vrchat
search for "halt"
theoretically you could hog the jukebox by pressing one button, waiting to the last second, and then pressing another
and since the thing never stopped playing, the ones you've already pressed stay off
so yeah, that part will come in handy
what you could do is add a couple seconds if you're the owner. Then if multiple people are trying to click it at the same time, it will always go to someone new
I'm surprised it's this complex to be honest
I was going to make a second event that just always played the audio source locally for the person who joins
and then edit the playjukebox event so that it clears the clip once it's done
does that make sense? it didn't work but you can see where I'm coming from
if nothing's playing, than the local play thing does nothing
if something is playing, the local play thing would play whatever was picked
I'm not following, sorry
but from what I understand, I don't think that's necessary. You might be overthinking it. If someone doesn't like the music they'll just turn down world audio
try adding a debug log right after calling audiosource play
make sure it's reaching that point
if it is, then it's some other issue like maybe the audiosource's volume/range is too low
do the other people see the interact disappear?
oh, do you not know about last build?
Oh right, forgot that was a thing
in the settings tab, enable "show extra options..."
for some reason I completely blanked that out on the menu
It's on there, I just missed it somehow
you can also use that to delay a second client without just using the go button
they will always go to the same instance
even if it's two completely different worlds, your PC will always use the same instance
I'm going to let it build anyway, I've had a lot of worlds break trying to cancel a build
yeah for sure
You only make that mistake once
alright, just need to check for you now
so the button stops being interactive and for some reason the sound plays on both clients now
just need to check about joining while playing
Oh weird, it kinda works but it plays the wrong song
did you click multiple buttons?
I did the first time, misclicked a song
One of the songs has a very distinct sound at the start so I've been using it to test
clicked the wrong one and then overrode it with the new one
yeah that's why you need to disable interactive on all of them. There is no guarantee they will arrive in the right order
That's probably why they did it in TF2 now I think about it
if you wanted to harden it against that issue even more, you could just set playing false on all the other ones
This is awkward on a modern game engine and a plugin designed for a multiplayer game, let alone an engine from 2004
no wonder they did it that way in 2008
eh, it's pretty straightforward. And tbh if I knew there would be multiple songs I would have suggested a different approach to begin with
like instead of a series of synced bools on separate behaviors, you could just have one behavior with a synced integer and an array of clips
I thought I mentioned that 😅
Either way, as long as it's not horribly inefficient I'll stick with this one for now
Is everything okay with the disable buttons stuff at the bottom?
I assume the parent variable has to be public and not synced
yeah that looks right. Unfortunately it's a lot more complicated than it should be. In a perfect world you wouldn't have that many nodes duplicated, but udon graphs don't support custom events with variables yet so ¯_(ツ)_/¯
oh wait, you need to plug the component array into the component[] get
I knew I was missing something, it was acting weird
Hmm, still acting the same
the button I press will only press once, but it still has the outline as if it can be pressed
make sure to hit compile after you finish. Compile happens automatically every now and then but if you immediately make a change and go straight to a build and test, it might not take effect
it's probably crashing from the missing component[]
yeah
i tried creating an animation that opens the door and even checked a tutorial i did nothing wrong but whenever i put the udon script the door stops moving
When a scripted component that implements the animator events is on the same gameobject as an animator, the animator assumes that it is now controlled by the script. I forgot what exactly you have to do in your graph to make the animator run as normal, but you can google it I think.
i got it same way as two tutorials without any single difference
however whenever i do it and run play mood it doesnt work at all and it stay still
i figured this out but how can u set networking to can pick multiple variables at once
Hi, total newbie to networking here; and honestly I’m not even sure I understand all of the possible use cases for it. I’m wondering if Udon Networking can be used to create object-property persistence in a world? What I mean by that is “once a player manipulates the properties of an object, it’ll retain those properties unless modified by another player. The object will snap to it’s last saved state when someone new loads into a new instance of the world.” Obviously multiple instances pose an issue; so additionally, I’m wondering if it’s possible to put an instance-cap on your world? My default assumption is no, but you never know.
so instance caps no. not posable anyone in vrc can create new instances
within a single instance an object can have propties that are syncronized to other clients and new joiners, most pickups already do it
For your first question, yes - the values of Synced Variables are shared within a world, but not between instances. Each instance starts with default values on all the objects. More details about Networking here: https://docs.vrchat.com/docs/udon-networking
Udon is slow, so it's likely that the automatic serialization will have less of a CPU cost than whatever method you make running in udon
Now that we have synced arrays, I don't think there's much of a reason to do your own serialization unless you're doing something like exporting to an input field that can be copy/pasted
Still missing string[] sync though :/ but we're not getting that.. sad
it's planned
I though you said network engineer said that he'd like people to not sync bee movie script
That's why I thought it's not coming
But that's great news.. hopefully I won't have to do so many stupid things to sync my string arrays 😅 😅
may or may have not already did that
May or may not have done that too when UNU was in Open beta
Synces quite nice
I mean we were supposed to test limits back then So I did
I remember I tried syncing megabytes of data at one point..
it's based on Network ID, which is currently based on path of the gameobject, yes.
Any plans to uh.. Change that 🤔 Changing hiearchy even a bit kinda breaks everything 😅 I often break like 10-20 instances whenever I update the world meaning 200 people are suddenly all in broken instances and need to rejoin 😄
I and ||(dupe)|| (2 users in world) try spawning an object, it's fine (I'm using take ownership then spawn.) and all code work PERFECTLY fine.
But in some situation (world with lots of people? or sth, IDK.) the code gone broken. IDK what actually happened.
Can anyone have some answer?
This is the code, if you wish to read. Also, as you can see, The RequestSerialization() on Update() is SUPER bad pactice, but If I use continuous sync. It doesn't work. any reason why also?
certianly not the most readable code iv reviewed before
well if i go peice by piece, OnOwnershipRequest can be removed as that the default behaviour
in start() you probaly dont the ownership check, you can just initialise it with a default array like PieceColor = new Color[PieceObjectPool.transform.childCount] (as color is a struct so will default to color(0,0,0,0) anyway and for manual behaviors it will override the value with the correct one one network data is send
to enumerate over childs of you object pool you can use .Pool rather then get childs
About Array, I thought that to sync, size must be same to all players first because is allocated dynamically ;-;.
So I misunderstand so much.
Or I write too much C.
the size of the array is now made up by the about of objects in the pool, which should never change at run time, only thier active states change
for which can use PieceObjectPool.Pool.Length
i notice here for (int i = 0; i < PieceObjectPool.transform.childCount; i++) if (SpawnedObject == PieceObjectPool.transform.GetChild(i).gameObject) you try to get the id of the object that has just spawned this can be simplified to Array.IndexOf(PieceObjectPool.Pool, SpawnedObject );
infact i should not complain so much about the code itzelf sorry, i just have a hard time to see what going on
I'm not good in coding and algorithm now, so It's better to complain it.
The concerning is still RequestSerialization() on Update() thing.
and
Where or why the code break, in unknown case.
the color array is probably just too big for continuous sync
I see, but is do it on Update() just f*** them more? Should I use brand new OnVariableChange thing on beta?
yes, doing it on update is not a good idea
onvariablechanged would work but if you're just changing the variable every frame the same way then you're going to get the same issue
I have no idea what you're doing because it's impossible to read when you have everything in the same line like that
It's not every frame though, just first time. like on code. No other change after that.
But I've tried set it only when created, but the color doesn't sync at all. So I have to make another variable to let that item sync the color again by update it from array.
I would love to give a more intelligent answer but your code style is breaking my brain
for (int i = 0; i < PieceObjectPool.Pool.Length; i++)
if (PieceObjectPool.Pool[i].gameObject.activeSelf && Networking.IsOwner(Networking.LocalPlayer, PieceObjectPool.Pool[i].transform.GetChild(0).gameObject))
{
PieceObjectPool.Pool[i].transform.GetChild(0).GetComponent<PieceProperty>().PieceColor = PieceColor[i];
PieceObjectPool.Pool[i].transform.GetChild(0).GetComponent<PieceProperty>().RequestSerialization();
}
well basicly you only need to request serialisation when made changes to any of the synched variables on the PieceProperty
fun fact you never actuallly seem to call RequestSerialization() after moddifing the PieceColor array
you shoulds only call it when a change is made
I've tried to do that. but not sync.
So I bring that to Update() and not do that in Spawn() function.
I may tried it again.
Or maybe refactor the whole code ;-;
well there's a difference between setting the synced variable on the owner's side and the other players applying that variable to some objects
it seems you need to RequestSerialization(); in your spawn, i see it there
but its missing from ResetPieceColor & DestroyAllPiece
ondeserialization happens when you receive synced data, you probably should use that to do stuff with the synced variables
but you could also use onvariablechanged
i refactored the code a bit to a point where i think its easyer to spot the problem
void Start()
{
PieceColor = new Color[PieceObjectPool.Pool.Length];
}
void Update()
{
if (AdvancedPaletteParent.activeSelf)
ColorPreviewRawImage.color = new Color(ColorValueSlider[0].value, ColorValueSlider[1].value, ColorValueSlider[2].value, ColorValueSlider[3].value);
for (int i = 0; i < PieceObjectPool.Pool.Length; i++)
{
var piece = PieceObjectPool.Pool[i];
if(!piece.activeSelf)
continue;
var piecePropertyHolder = piece.transform.GetChild(0).gameObject;
if (Networking.IsOwner(Networking.LocalPlayer, piecePropertyHolder))
{
var pieceProperty = piecePropertyHolder.GetComponent<PieceProperty>();
pieceProperty.PieceColor = PieceColor[i];
pieceProperty.RequestSerialization();
}
}
}
public void SpawnPiece()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
Networking.SetOwner(Networking.LocalPlayer, PieceObjectPool.gameObject);
if (!Networking.IsOwner(gameObject) || !Networking.IsOwner(PieceObjectPool.gameObject))
{
StatusText.SetStatusText(3); return;
}
Color color = GetColor();
if (CheckColorDuplication(color))
{
StatusText.SetStatusText(2); return;
}
GameObject SpawnedObject = PieceObjectPool.TryToSpawn();
if (!Utilities.IsValid(SpawnedObject))
{
StatusText.SetStatusText(1); return;
}
Networking.SetOwner(Networking.LocalPlayer, SpawnedObject);
Networking.SetOwner(Networking.LocalPlayer, SpawnedObject.transform.GetChild(0).gameObject);
var pieceId = Array.IndexOf(PieceObjectPool.Pool, SpawnedObject);
PieceColor[pieceId] = color;
RequestSerialization();
StatusText.SetStatusText(0);
}
public void ResetPieceColor(GameObject obj)
{
var pieceId = Array.IndexOf(PieceObjectPool.Pool, obj);
if (pieceId == -1)
return;
PieceColor[pieceId] = new Color(0, 0, 0, 0);
RequestSerialization();
}
public void DestroyAllPiece()
{
if ((Networking.GetOwner(Core.gameObject) != Networking.GetOwner(gameObject)) && Core.isLocked)
return;
for (int i = 0; i < PieceObjectPool.Pool.Length; i++)
PieceObjectPool.Return(PieceObjectPool.Pool[i]);
for (int i = 0; i < PieceColor.Length; i++)
PieceColor[i] = new Color(0, 0, 0, 0);
RequestSerialization();
}
Color GetColor()
{
if (!AdvancedPaletteParent.activeSelf)
{
for (int i = 0; i < BasicPaletteParent.childCount; i++)
if (BasicPaletteParent.GetChild(i).GetComponent<Toggle>().isOn)
return BasicPaletteParent.GetChild(i).GetChild(0).GetComponent<RawImage>().color;
return new Color(0, 0, 0, 0);
}
return new Color(ColorValueSlider[0].value, ColorValueSlider[1].value, ColorValueSlider[2].value, ColorValueSlider[3].value);
}
bool CheckColorDuplication(Color color)
{
for (int i = 0; i < PieceColor.Length; i++)
if (Mathf.Abs(color.r - PieceColor[i].r) < .2f
&& Mathf.Abs(color.g - PieceColor[i].g) < .2f
&& Mathf.Abs(color.b - PieceColor[i].b) < .2f
&& Mathf.Abs(color.a - PieceColor[i].a) < .4f)
return true;
return false;
}
probaly unrelated but i have no idea how CheckColorDuplication is seposed to work that is either broken or some high level math i cant grasp
The CheckColorDuplication() which have reference in SpawnPiece() function is to check that is color of the future created piece will have the color from GetColor() - function to get color from player's choice. too similar to PieceColor[] or not. I use +- 0.2f threshold. Except the alpha I use 0.4f.
oh i see now, yes that will work, you check if the new color is more than .2f different from any existing color
well for your problem, if it break with a lot of poeple it can indeed the that your calling RequestSerialization for each PieceProperty that is both active and your the owner of, if you try to serialize to much you hit throttling
So If I just put the color in PieceColor[] - synced manually and let PieceProperty get the color from it - unsynced. Should it will work in more dense condition?
Oh btw.
The PieceProperty Things.
i see. well the better way is to get rid of PieceProperty completly than
keep PieceColor[] manuelly synched, and make sure you always RequestSerialization() when you moddify it (so also inside ResetPieceColor & DestroyAllPiece)
then after each call to RequestSerialization() add a call to OnDeserialization()
so you get
RequestSerialization();
OnDeserialization();
and in OnDeserialization() (it gets called on all other clients as soon as new information about the PieceColor[] is recieved) you can there update the materials
and then in the deserialization do
//inside PieceSpawner
void OnDeserialization()
{
for (var i = 0; i < pool.Pool.Length; i++)
{
var piece = pool.Pool[i];
BaseRenderer.materials[0].color = PieceColor[i];
BaseRenderer.materials[1].SetColor("_EmissionColor", PieceColor[i]);
ConeRenderer.material.SetColor("_EmissionColor", PieceColor[i]);
}
}
or you could do a call to PieceProperty instead
void HandleDeserialization()
{
for (var i = 0; i < pool.Pool.Length; i++)
{
var piece = pool.Pool[i];
var pieceProperty = piece.GetComponent<PieceProperty>();
pieceProperty.UpdateColor(PieceColor[i]);
}
}
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class PieceProperty : UdonSharpBehaviour
{
[SerializeField] Renderer BaseRenderer = null;
[SerializeField] Renderer ConeRenderer = null;
void UpdateColor(Color pieceColor)
{
BaseRenderer.materials[0].color = pieceColor;
BaseRenderer.materials[1].SetColor("_EmissionColor", pieceColor);
ConeRenderer.material.SetColor("_EmissionColor", pieceColor);
}
}
that way you can still keep the references to BaseRenderer & ConeRenderer inside the PieceProperty but get rid on the update() there as it will now only update the color when new color information is received
Thanks for the idea and coding. Hope this works.
How can i ask for a variable from the Master? i wanna ask for a Int when the someone starts the world
well you cant ask for a variable, they variable need to be marked as syncronised, and then it will be send to other users
depending on whether or not you set the syncronisation to manual or continues
also the variable will be controlled by the owner of the object the udon behaviour is on, this does not neccerly needs to be the master
Should I put OnDeserialization(); after RequestSerialization();? (I've already set for self.)
When I didn't put OnDeserialization(); It doesn't sync most of time.
Put the setcolor in Update() make it worse.
Didn't try when put it on yet.
public void SpawnPiece()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
Networking.SetOwner(Networking.LocalPlayer, PieceObjectPool.gameObject);
Color color = GetColor();
if (CheckColorDuplication(color)) { StatusText.SetStatusText(2); return; }
GameObject SpawnedObject = PieceObjectPool.TryToSpawn();
if (!Utilities.IsValid(SpawnedObject)) { StatusText.SetStatusText(1); return; }
int id = Array.IndexOf(PieceObjectPool.Pool, SpawnedObject);
PieceColor[id] = GetColor();
RequestSerialization();
SpawnedObject.GetComponent<PieceProperty>().SetColor(GetColor());
Networking.SetOwner(Networking.LocalPlayer, SpawnedObject);
StatusText.SetStatusText(0);
}
public override void OnDeserialization()
{
foreach (GameObject gobj in PieceObjectPool.Pool)
if (gobj.activeInHierarchy)
gobj.GetComponent<PieceProperty>().SetColor(PieceColor[Array.IndexOf(PieceObjectPool.Pool, gobj)]);
}
This is the some part of code.
yes OnDeserialization after request serialization
aww so reason it's buggy because I didn't called the function?
I tought it'll called automatically after synced data is sent 🤣🤦♂️ .
so OnDeserialization is a bit wierd that it gets called when new synchron7zed variables are received over the internet, however for the owner who request the new variables to be syncronised it never gets called as he already has the data
therefor if you want some processing to happen when new vales are available you would put that in OnDeserialization, but ypj have to manually call it for the object owner
I have
SpawnedObject.GetComponent<PieceProperty>().SetColor(GetColor());
To called for the owner.
But it don't sync remotely like remote OnDeserialization() is not called for remote player.
yes so in on deserialization is when a remote clients gets new color info and should update all colors
Still not latest sync,
Case (1) 2 pieces spawn from a player who is already owner. (Cyan and red.)
The weirder is it's white, not 0,0,0,0.
Spawn the third onemake it sync the first 2 properly, but the third one is white.
Case (2) Alternate the owner in each pieces, act same as first case.
White color comes from original prefab's material color.
Right now, I'm solved by adding delay function to sync the color again.
Hello! So, I'm really trying to understand how syncing works, but as much as I try I can't understand it in any way. So, I'm trying to do something really simple, when someone press a button their name will show on a board for them and everyone else (obviously even for late joiners); my idea for now is to have 2 synced variables, one is an int array where I store the id of the player (for what I've read strings are really heavy and slow to sync, that's why I'm using the id) and one to know how many have pressed the button. So far I've successfully made it that the owner can press the button and their name appear and it appear for late joiners, but I can't manage to transfer the ownership/send data to the owner and send to everyone in the instance the name. What's the best way to do this?
public class SettingsUno : UdonSharpBehaviour
{
public UnoGame MainScript;
public int maxPlayers = 4;
public Text[] playerList;
[UdonSynced] private int[] playerOrder;
[UdonSynced] private int playerCount;
VRCPlayerApi localPlayer;
void Start()
{
playerOrder = new int[maxPlayers];
localPlayer = Networking.LocalPlayer;
}
public void joinGame()
{
if (playerCount < maxPlayers) {
Networking.SetOwner(localPlayer, gameObject);
playerOrder[playerCount] = localPlayer.playerId;
RequestSerialization();
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(sendNamesAll));
playerCount++;
}
}
public void sendNamesAll()
{
playerList[playerCount].text = VRCPlayerApi.GetPlayerById(playerOrder[playerCount]).displayName;
}
void OnDeserialization()
{
for(int i = 0; i <= playerCount; i++) {
playerList[i].text = VRCPlayerApi.GetPlayerById(playerOrder[i]).displayName;
}
}
}
This is the code so far
Das OnDeserialization work? i mean on my code it just create a loop
As far as I know, OnDeserialization will be called on every update if you use the continuous update, if you want to update a value just when you need it you should use manual and use RequestSerialization() when you want to update it, if that's what you mean
Look at the code I've posted before your message, you just need to put RequestSerialization() when you need to update the synced variables, but well, my script works only for late joiners, not globally, so take those info with a grain of salt, I'm here because I need help too lol
ok let me test!
Yep still
is a loop
i don't get it
i'm just doing a
void OnDeserialization()
{
Debug.log("Test")
}
and a RequestSerialization(); on start
Take a look at the SyncButtonBecomeOwner example in the UdonExample scene - this shows how to easily have anyone take ownership of an object and change a value on it, in this case a counter for the number of times it's been clicked. Note that if you're only syncing the id of the player, they might be gone when someone else gets the data, so you wouldn't be able to resolve from that id to their displayName. I'd recommend syncing the displayName string itself if that's what you want.
@errant siren Do you understand my problem? when the world runs the OnDeserialization() is being call every second
and i have a RequestSerialization(); on start
ondeserialization happens frequently if you have the udonbehaviour set to continuous sync, yes. That is intended
continuous sync does not require requestserialization
i'm not able to understand why that is not working
manual sync is the one that requires requestserialization and will only happen once when you manually call it
ehhh well it looks like you have other issues
do you have an objectsync on this object?
someone help me with avatar stuff dm me
or other udonbehaviours?
check out the #avatar-help channel
I have a main script that runs a second script
Do i need that one on Manual?
you need to make sure that all udonbehaviours on this gameobject are set to manual sync if you want to use manual sync
you cannot mix sync types between udonbehaviours on the same gameobject
can you show the whole gameobject?
yes
you have no other components?
and a Mesh Renderer
you checked really quickly, are you actually trying this ingame or did you use cyanemu?
and you're getting that debug log "Test" frequently?
well update is supposed to happen every frame, that's what update does. Or are you talking about OnDeserialization?
OnDeserialization is happening every frame
In the end I solved the problem thanks to Phasedragon, the script was already kinda working but I had to check the validity of the player, so the script was crashing, and thank you for the information! Just out of curiosity, the ID are unique for every player in the instance or they will be assigned to another player in the instance when they left? I'm not sure if this can be a problem for what I need, but just to make sure
and you know that because of the debug.log("Test")? That's a pretty generic debug log, are you sure you don't have another script printing that as well?
or maybe you have something in your script calling OnDeserialization directly?
Yep
No i don't
i just have a RequestSerialization();
But now i don't havbe a Debug.log("Test") any more
i have the actual thing that it has to do
Where exactly are all the places you are calling RequestSerialization? Is it at all possible that you are making that happen from update?
could you share a bit more of the code? I have no idea how this is possible with how you've described it
sure
Sorry if this is a silly question, but are events like OnPickup() and OnDrop() only called on the client that’s doing the picking up and dropping, or do they run for everyone?
they only run for the person who grabbed it
That’s a relief! Is there a good reference or rule of thumb for what events execute locally vs universally?
Most things are local apart from some aspects of video players and stations
OnPlayerTriggerEnter is only really triggered when the local player enters the trigger volume right? Like if client A and B are in the world, and A enters the trigger volume, then the code will not execute on client B's local version of the world right?
It will trigger when any player enters it on the local client
You can gate whether it triggers or not by checking if the player that entered is the local player
Thanks, was wondering if I needed to branch on Get isLocal or not!
Alright. So I've created a menu that follows every player around in a map, one of the options is to edit strings that can be seen by other players, using a manual serialization.
Now the problem is that the master's synced values can be seen by others, but that others' values can be changed, but only be seen by themselves. A bit of a shot in the dark, but does anyone have any suggestions on what it could be?
vrchat's networking page mentions that you should try a setowner before doing the change, but that doesn't seem to work.
yep, that's exactly what you need to do . Check out the synced text field example in the UdonExampleScene to see it in action.
Hello again! Still struggling with networking and the VRC Object Pool, so for context, I have the cards in the object pool, all the cards have Object Sync, and if I make the owner of the game (player 1) spawn and position them it works correctly, but I have only one problem with this. The cards are made with an UI, so each player have a canvas where the cards go, so I can use a grid to align them. Now, I can't actually make the event networked because of the Object Pool, it throws error because the other players are trying to spawn when they are not the owner, and even if the owner spawn the cards, I can't parent them on the canvas on the other clients because I don't have a reference of the object spawned for the other clients. Hopefully it's not too confusing, but long story short, is there a way to parent a spawned object for all the clients or at least for the right player? I've tried even switching the ownership when you spawn the cards, but for some reason I can't make it work, I just really need that each player have the ownership of their cards and they are parented for their screen 🤔
You can add OnEnable and OnDisable events to the cards, those will happen when they get spawned and despawned by the pool. You can then do whatever you want during that event and everyone will see it
But how do I know from the card on which screen should it go? I mean, for the parenting, not the position
you could have a synced int array on the same object as the pool which represents some information about each object
Oh, right 🤔 I wanted to use less synced variables but well, I guess I don't have other choices, I've tried for 2 days now with the method I was trying lol
Thank you so much for the help!
I would recommend just having a "version" number somewhere and if players disagree with the master, put a message somewhere that they are not on the same version and they should rejoin
well obviously it's not going to work for players that don't have the version number. But once you have established it, any further version upgrades will use the same synced variable
it would help to have the version number gameobject at the top of your hierarchy, since it assigns network IDs from the top down
What's a good resource to learn all the new Udon Networking Update stuff?
Networking question; say I have a custom event I plan on sending over a network with SendCustomNetworkEvent - will that event's code execute x amount of times, where x is the number of clients that receive the event? Like for instance if the network event has code that teleports player A, would player A be teleported 3 times if there's 3 players on the server at the time that event is fired?
yes to multiple executions, however you cant teleport other players only the local player so you wont be teleported 3 times
hm? Are you sure? I seem to recall hearing that teleporting other players does work...
oke i might be wrong than
I mean the teleportto API will teleport any player that you feed into it
No it does not. Its confusing because it even accepts a player api, but it only works locally. If you try to teleport a remote client you will get a message saying something along the lines of "failed because client x was not local" or something like that.
VRChat has a lot of these smaller inconsistencies and weird design decisions in their public API unfortunately.
Any way to get a player's ping using Udon? My best guess is Networking.CalculateServerDeltaTime but I really can't find any documentation on it
No wait I just remembered what deltatime is that's not it
No you cant get ping
Get the server time on the owner of an object, set it as a synced variable. When it deserializes on another player, compare it to the server time there - that will give you the end-to-end delay.
The latest UdonExampleScene in the Open Beta has a simplified video sync program which does this to sync video players
Wow. Thats an unconventional way to sync lol
the new program? Or that way of calculating end-to-end delay?
Using ping to sync
you would prefer to sync video players without accounting for delay?
I have a much better method. And so does the U# player
there's more going on than just that - you're welcome to look at the graph to see the logic
Also an easier way to calculate your own ping is to set a boolean, set a timestamp (using the timedate stuff from C#), send a custom network event to all, and only respond to the event if the boolean is checked. Im pretty sure that Network events are bounced no matter if you locally receive it as well. Then you just compare the current time to the previously saved timestamp.
that doesn't tell you anything about someone else's ping, though. If someone joins your world 5 minutes after you started a video with a delay of 200ms from you...
The new example prioritizes:
- Simplicity - so it can be understood / modified
- Low-bandwidth usage - if no shuttling / pausing is performed, you can sync everyone with a one-time manual Vector2 per playback
Many video worlds are synchronizing WAY more often than necessary, sending ridiculous amounts of data
Well most of those are also still not updated to manual sync
And I dont even understand why ping matters at all. The way the "old" example and also my player works is that you get the time delta between when the owner started playback, and the current time. Which means that ping doesnt matter. Youre simply skipping over the first few hundred milliseconds of the video.
yep - that's very similar to the new example. It sounds like the difference may be in using a Vector2 instead of a single variable - with x being the network time when the variable was updated, and y being the video time. So the owner can jump to another spot in the video and update the variable to get everyone in sync to the new point
Yeah thats pretty much the same method I use, but I call them "SyncTime" and "PlaybackOffset".
And I dont like bundling them in a Vec2, because thats confusing. They do belong together, but they shouldnt be treated like a vector.
it guarantees they arrive at the same time, in the right order, and I can use OnVariableChange to detect it
Agreed that it's a bit of a hack but it works well
With manual sync I have never seen variables that are changed and serialized together, not arrive together
Ideally you would be able to make your own struct
but yeah...thats gonna be a while.
that's gonna be never - IL2CPP doesn't allow it 🤷♂️
Could also use a float[2]
Arrays don't work with OnVariableChange events
since it can only detect a change to the reference and not the members
Well yeah I dont use OnVariableChange, because I want to rely on VRC's API as little as possible. Simply so I can predict the code better.
However it does increase the execution time, which is probably not worth it.
Also I use Unix Time in form of a Long as my sync time, so I cant use a Vec2 ¯_(ツ)_/¯
What if I create an entirely new array for the change?
then the devs will hunt you in your dreams
public void loadUrl()
{
videoPlayer.Stop();
videoPlayer.LoadURL(URLField.GetUrl());
SendCustomNetworkEvent(NetworkEventTarget.All, "loadUrlNetwork");
}
public void loadUrlNetwork(VRCUrl url) {
videoPlayer.Stop();
videoPlayer.LoadURL(url);
}
``` how do I pass an argument through a custom network event?
You dont.
🙂
(Maybe at some point in the future this will be possible, however at the moment it is not. Your next best option is manual synced variables.)
just keep in mind manual synced data can arrive more times than it's sent
The trick to do it is set a value on the receiving Udon Behaviour, and then fire a network event at the receiving Udon Behaviour telling it to do whatever with the value.
Eg; If I have five different ways something can add to a centralized game score with different point values, from the scoring object I apply its internal value (say; 500 points) to a generic AddValue int in the receiving Udon Behaviour handling scoring. I then shoot "UpdateScore" at it, and in UpdateScore it can play with the value however (eg; calculate against multipliers, run anything fancy displaying the score increase, etc).
youre not guaranteed to receive the value, before receiving the event. It might work 99.999% of the time, but its not guaranteed. So its up to you, if you want to rely on it or not, but its not intended that way.
how do I use manual sync in udonsharp?
take ownership with Networking.SetOwner(Networking.LocalPlayer, gameobject);
set a synced variable
call RequestSerialization();
And yeah, definitely don't rely on manual sync to arrive first, especially with large amounts of data like a string or a URL. If the network is clogged and lots of other data is going through, the manual sync will be delayed but the network event will not.
If you want to detect when the manual sync, arrives, you would use OnDeserialization. When that event happens, you can safely read synced variables and do whatever you want to do with them.
Alternatively you can use OnVariableChanged if you care about a specific variable changing instead of just any variable, though it's a bit more complicated in udonsharp
so if it's clogged will it delay the arrival or not arrive at all?
it will delay the arrival
so if I want to make a video player should I make it report back to the owner that the URL was received, and only start playing once everyone in the room has received the URL?
That would be a good safety measure to have, yes. In addition you may want to wait until they have actually finished loading the video.
some people have much slower internet than others and this can make multiplayer video watching very complex
Making a good video player requires quite an extensive knowledge on networking and the video player api itself. If you actually want to make something to use it and not be annoyed by it constantly breaking, I wouldnt recommend doing it yourself. If you just want to learn while trying out something new, then its still quite an advanced project.
My current main video player script has 1300 lines of code and its still not quite finished. (granted about 20% of that is empty lines or braces, but still)
I need to trigger it playing and stopping from a different script and need to constantly get the time elapsed on the video to make it sync with something else in my world, so I thought I might as well make it myself, it doesn't need any features like volume adjustment or skipping around
is it a lot more complicated than implementing the load confirmation to make it work reliably? if it is then I might just use something else
It is a whole lot more complicated than that. In essence its quite simple. But there is a lot of situations in which it can break, if you dont prepare for them.
Im still working on my modular player, otherwise I would have referred you to that. But I mean its always good to take on a challenge, cause you will always learn something from it
is there anything already made I could use then? something like U# video player works well but has stuff like pausing, changing the video time, ect... which is nice for a normal video player but not for what I want to use it for
Well I mean you can just strip that stuff out of it...if you can figure out what is and what isnt needed :P
If you want something super specific, then yeah you will have to make it yourself. But another option is to take something generic and shape it into what you need, but taking bits off and adding new things onto it.
Unfortunately Merlins player isnt super modular, and not very well documented. Which is why Im working on a video player myself.
alright well when you finish it let me know, I'll try to make this work in the meantime
I have the core part of it working. I just need to do some more exotic stuff, like supporting livestreams, handling protocols like RTSP, Making it so that other people can take control over the player, making an options UI
And ofcourse documenting the entire thing...
I don't really need any of that, so if you want could you send me what you have atm? you don't have to of course if you don't want to
Its not polished and tested enough for that yet, sorry.
alright, let me know when you finish it then
the new UdonSyncPlayer example released in the Open Beta is made to be very simple and approachable, you could probably use that as a base and add any features you need on top of it.
handles:
- Anyone changing URL loads new URL to everyone else (can turn on/off from checkbox on program)
- Syncs time between everyone, with variable rate (every 10 seconds, every 5 seconds, etc)
If I make a variable synced(manual) and set it with send changed disabled, then some other udon behaviour change the variable, does on variable change triggers for everyone?
setting a variable from another udonbehaviour requires using setprogramvariable (or in udonsharp, it would be compiled to that anyway) and setprogramvariable always triggers a change anyway
however, triggering a change does not necessarily mean it will be synced to others unless you do requestserialization
forgot about saying, request serialization is set
I had a very strange issue with late joiner
also you should be aware that sendchange is really only a local preference. For remote players receiving synced variables, it will always cause onvariablechanged even if you have sendchange disabled
On variable changed worked every time for existing people whenever late joiner come in
I made a node with when a boolean changes to true On variable change trigger, and although I set the boolean to false, whenever late joiner come in boolean become true for existing people and on variable change worked
did you request serialization after setting it to false?
No but I changed via send network in other nodes
is it a synced variable?
then you should use requestserialization to sync it, not network events
I checked the log and it saids set properly(log after on variable change)
I think It is fixed by requesting serialization
But log says send network works same way til someone joins the world
if the owner tells you it is true, then you receive a network event that causes you to set it to false, that doesn't actually set it properly. The reason it resets back to true is because you will re-apply synced variables back to whatever the last time you heard that it was, which is true
not when it comes to setting synced variables
you should never set synced variables if you are not the owner
Oh
just because you received an event telling you to set it, doesn't mean that the owner set it
I got it
That’s exactly what happens
Thx and I have one more question
- Calls to
SetVariablewithsendChangeturned on will now actually becomeSetProgramVariablecalls under the hood so that both methods of triggeringOnVariableChangeevents work the same now. Fixes an issue where the 'old' variable value was incorrect on first access, and an issue where value types were never copied over
I experienced the last line “value types never copying” and this happened when I make set variable by other variable, const worked fine. Is there a way to bypass this bug? Its fixed on beta sdk but I can’t wait for 2019..
you can just use setprogramvariable instead of setvariable
On same node?
So how do people generally handle multiple users trying to change synced variables on the same object?
Im still working on my Video Player and Im not sure how to handle say 3 people changing the URL at roughly the same time.
The best way I can envision is a first-come-first-serve policy, where you only allow the first user to take ownership, let them change the variable, and only then accept ownership changes again on their end. But Im not sure if that really is the best way to handle that.
I cant get objects to synchronize. someone told me to just add a udon component but didn't help. Should this really be it or should I do it another way?
If you mean you want their position to sync, add a VRC Object Sync component
no, you don't. It used to be that you set this on the UdonBehaviour but these days you only need VRC Object Sync
cool cool, Thanks :D
If you want to learn more about networking, you can find lots of info here: https://docs.vrchat.com/docs/udon-networking
The three main concepts used for networking in Udon are Variables, Events and Ownership. Variables are containers for values - like a number, a set of colors or a 3D position.Events are things that happen at a moment in time.Ownership is the system that decides which user can update a variable, wh...
Perfect thanks
I have a clarifying question about RequestSerialization
https://docs.vrchat.com/docs/udon-networking#requestserialization
If the owner of an object calls RequestSerialization a massive amount of times in a short time, then that's not an issue right?
Because the owner won't actually do any more work than serializing once per network frame
As I understand it, "request" just means it schedules serialization to happen at the next network frame. So if I call it 40 times between network frames, the networking burden (and CPU burden) is still the same as if I'd called it once?
sounds sketchy, and a waste of cpu time. but you might be right about it not affecting network traffix
but also be aware that manuel sync is faster then continues sync, it sends its data basicly almost directly
That's correct. It's just a request, and flags it to be serialized later. Requesting more times doesn't do anything
So I think I found a new "Networking Quirk":
I tried to set a synced variable on a manually synced behaviour and call RequestSerialization() immediately in OnOwnershipTransferred()...
It only worked locally. However when delaying the event that does the setting and requesting by 1 second it works for all players.
So seems like there is a problem with that, or I just did something wrong. But Im fairly certain at this point that there is something not working as intended.
Just wondering what happens if you claim ownership right before requestserialization?
it will just work
the typical flow is to take ownership, set a variable, then requestserialization
so that is very normal
yes, it's a little bit different though. There is documentation
the basic way of doing it is to add FieldChangeCallback
however, the tricky part is that if you just set a variable by doing x = y, this will not trigger a change. If you use SetProgramVariable, however, it will trigger the change
if you want to trigger a change without using setprogramvariable, then you can instead run it through a property, which is a new feature that udonsharp supports and you can see the example there as well
properties allow you to do things when you try to set or get a variable
ok thanks
i though x = y did work in udonsharp as it compiled down to setprogramvariable
ah i see the documention on this was updated
so you mean on variable change works with just x = y?
no, it does not
graphs compile into setprogramvariable and there was a changelog about that, that may be what you're referring to. But udonsharp does not
He is talking about the udon 'node' changelog in beta right? got it
Does request Serialization affects other udon behaviour(or u# behaviour)? I have a strange acting deserialization on two udon behaviours on both udon node and #
It does if they are on the same gameobject
Udonbehaviours on the same object will be paired and share networking
I have two seperate object and seperate udon behaviour(lets say A, B with manial sunc) A have int value ‘a’ synced B have boolean value ‘b’ synced, changing a to like 1 to 10 syncs fine and the problem is after that if I change b like false to true, changing b only works for owner of b and later someone changes a than a, b is synced together
Solution was
class B : Udonsharpbehaviour{
public B _self
Void dosomething(){
_self.RequestSerialization()
}
}
Like this and put B itself in inspecters public B solved this in u# but udon node didn’t worked for me
You're gonna have to rephrase that, I'm not following at all
But I can say that _self.requestserializstion is not necessary. You can do just requestserialization and that will always refer to itself
It sounds like you're saying the problem is that non-owners can't change synced variables? If that's the case, then yes that's exactly how it should work. You need to take ownership before setting variables
I'm not exactly sure what is the problem, but basically the "RequestSerialization()" will request sync for once for the UdonBehaviour it called on. Sync variables value can only be modified by the owner of the object, so like Phasedragon said, you need to use Networking.SetOwner() to set its owner to local player to change the sync variable value(s).
And I'm not sure but I think this request sync function will only work if being called on the client of the owner of the object.
I'm having an issue where I have 1,000 brokered sync objects (things that don't have any updates and it can run at 144 FPS) but, sometimes after cycling through a a bunch of players, the positions of some objects are lost to new players. Is it possible that calling a Networking.SetOwner( Networking.LocalPlayer, gameObject ); then having that player leave could leave the object in an unsyncable state? Is there an event when this happens, so that I could own the object back to the instance owner?
I've been looking into this recently and it can definitely take some time before you successfully receive all manual syncs when you have a large amount. A good way to see this happening is to add some kind of visual thing that happens when each object gets OnDeserialization, like a particle burst or a material change. If you do this, you'll see it slowly move in a wave across your objects. It does so in a predictable order, probably based on network ID which is based on the order in hierarchy.
I'm not sure how you have it set up, but if the unused objects are doing requestserialization at any point then that would definitely cause a bottleneck. You might want to consider putting your objects in a pool so that there's no chance that the unused cubes will use up your bandwidth
Friendly general purpose reminder related to above: Put the most important objects that require syncing at the highest point on the hierarchy.
eh, yeah technically but it only really matters if you have a lot of manual synced things
also that is definitely not defined behavior, don't rely on it staying the same
Maybe not defined, but strongly anecdotal.
sure
at least at this point.
We are testing another build, but, what I was seeing was the objects, if they were going to update would have been updated at start. Otherwise they literally never update. Still going to see on this new build, though.
Yeah, not sure what's up there. Just letting masters slowly sync all their objects fixes everything.
FWIW, on live there are certainly cases where Object Sync misses syncing some objects as well. My otakaze mahjong world seems to repro this frequently - if you have four people playing mahjong, late joiners will usually see a large fraction of the tiles in their original positions - a significant fraction will sync almost immediately, the rest will not sync even if you wait for tens of seconds, at least. Moving the tiles (actually, rotating them) by hitting the resync button usually fixes the issue (note that this rotates all tiles).
World in question: https://vrchat.com/home/world/wrld_685838b2-dc34-47c7-8adf-92d5016075dc
Once 2019 is out I plan to be working on this world a bit more, so if I can find a consistent repro procedure I'll put in a canny.
Having way too much trouble getting a simple toggle synced. Basically I have two main objects, with two associated UdonBehaviors.
SteeringWheel: A VRC_Pickup which has a reference to the Particles Udonbehavior. OnPickupUseDown it sends a true value to the Particle Object. OnPickupUseUp it sends false.
Particles: A Gameobject with 3 children particles gameobjects. It checks the ParticlesState variable. and enables or disables the children
pickup events are not synced, so you'd have to either make them network events or use a synced variable.
the variable I'm updating is on the Particles, and is marked Synced
does that not make them a synced variable?
are you sure you're the owner of that object?
also it's very wasteful to constantly set it on update, that's every frame. It would be better to just use onvaluechanged.
ah good call.
So I should set owner of the particles object when I pickup the steeringwheel?
yes
also might as well make the particle object manual sync so it's not constantly using bandwidth. If you do that, you'll have to call requestserialization after setting a variable
I think I'm switching the particles to use CustomEvents and I'll call them from the steeringwheel on use down/up
should be lighter that way
OK this is getting a bit maddening, I'm trying to have a youtube link set to a button, so when the button is clicked it sends the URL to the video player and starts playing it. I'm using WolfeVideoPlayer and a script called KaraokeNorixMod that should pass the URL over to the video players URL input field. For experimentation, I have used jetdogs synced switch prefab, that should trigger a basic cube that turns on, with the script attachedto it. I cannot get it to do anything, despite having no compile errors, at a complete loss as to what I'm doing wrong.
(Cube appears) video player does nothing
alternatively, I guess the only other way is to make a video player for every URL I want, and have it trigger a new video player for each one...seems terribly inefficient to do that though
it's unlikely that all those prefabs made by different people are just going to work together without any modification. It's likely that the video player requires more than just setting the URL. maybe it needs some event to tell it to play, idk. Depends entirely on how it's set up
the script was made for this particular player (no instructions of course) I'm just using jetdogs script because it's 'easy' and I don't want to re-invent the wheel to just turn something on and off (that works) Is there any way to actually 'see' what udon is doing in the background step by step? It's really hard to troubleshoot these networking issues when there are no errors
not ingame, no. But you could look at the video's script and see what it is expecting
The video player itself works, when i type/copy/paste the URL into the field. I just want the button to populate that field because i'm growing sick and tired of having to pull off my headset to put in a URL from a web browser constantly :D
right, so look at the current input field and see what events it is sending to the video controller
ok I'll take a look
ugh way beyond my skill level....I'll just forget this idea and have it spawn a new video player for every URL, and remove the other one when a new one is chosen, way too difficult to try to figure this out (thought this would be simple, but I guess it isn't)
could you at least take a picture of the input field's inspector?
no, I mean the input field that's built into the video player
the inspector of it?
I don't think that's the input field
wait what
yeah I don't get it either XD
that doesn't have any events, it shouldn't work
unless the video player is constantly looking at it waiting for it to change
can you try the udonsyncplayer in the udon example scene? It's built into the SDK
this is what that one looks like
Yeah, I think maybe that would be better, I'll modify the script to work with that one
it sends a custom event "OnURLChanged" to the video player
I'm just using this one because someone recommended it to do this sort of thing...it looks overly complicated
a lot of video players are
allrighty, I'll give that a shot XD
what are you trying to do?
make a button that changes a video player's URL
Looking at it, I think it's going to be far easier to just put a seperate video player with an autoplay URL pre=coded into it, and just make toggle buttons to turn on the appropriate player, and turn off any others that are already on. Thanks for the suggestions though.
then you'd have to sync which video player is active
I have another question about synced variables and object sync stuff:
I have a prefabbed object in my world, that if you press a button, a script will instantiate a clone of the object. The object has that VRCObjectSync on it to update its location and stuff. If player 1 joins the world and presses the button which creates a clone, and then after that clone has already been instantiated, will it exist for player 2 if player 2 joins after the fact?
Also, if it does not exist for player 2, if the object fires a network event in player 1's world, would that cause errors for player 2 if the object does not exist?
instantiated objects do not support syncing
They don't? Dang
There's a workaround for it though, instead of instantiating, you can have them in an object pool and SetActive() them to do the same thing. But to achieve the sync you are looking for you also need another sync variable that keep track on which object is active and which is not. So when the new player join, they can receive this sync variable information and set active/deactive based on the info.
the vrc object pool script does that for you automatically
😅 Okay, didn't know such a thing.
I just very don't like any auto sync functions delay so I coded all my system based on manual sync.
vrc object pool script? I'll need to look at that
I've been wrestling with a system of watcher objects for players in my world, I need a watcher that follows a player around and tracks their position, which is simple enough, but I need someone so that players can join/leave a world instance frequently without breaking anything
^ ikr, that happens all the time. Player missing null reference, player in the world but didn't get recorded. I spent like a week trying to eliminate those weird errors but in the end I made a self-check-fix function to do that for me automatically. 🤪
What does object pool DO exactly? I haven't been able to find any documentation on it.
Yeah, that doesn't actually tell me how to do anything with the actual object pool. The example's for working around not having one yet.
That points to the legacy docs for some reason, here's the up-to-date version: https://docs.vrchat.com/docs/network-components#vrc-object-pool
This doc covers Networking Components, Properties and Events you can use in your Udon Programs.Special properties you can get from Networking: IsClogged - returns true if there is too much data trying to get out. You can use this to hold off some operations or adjust your logic. IsInstanceOwner - re...
Between those docs and this example, I hope you can learn how to use it: https://docs.vrchat.com/docs/udon-example-scene#objectpool
This scene is ready to Build & Test or Publish, and it demonstrates many common interactive items. Prefabs These objects show off some of the Prefabs included with the SDK which demonstrate default interactions with the VRChat components for Avatar Pedestals, Stations and Mirrors. AvatarPedestal...
so basically its a glorified synced bool[]? 😛
that's the heart of it, yes.
it's more efficient than just a bool[] because it packs the bits more efficiently. A normal bool[] actually uses up a whole byte for each bool
Cheers for that, I think I get it now.
Quick question, is there a way to get the VRCPlayerAPI of a player who recently called an SendCustomNetworkEvent?
Morning brain is going brrrrrrrr
Yeah I'll go ahead and upvote those. Guess I gotta default back to my "OnPlayerJoined" userdata, and get when a userdata does the thing, we know which userdata can receive the playerdata.
Bleh
(I was secretly hoping since VRChat literally displays the user who called the event in the debug log, that we'd have access to that by now.)
A jank workaround would be to take ownership of the udon behavior and then send the custom event
You could then query who the owner of the object is
Jank indeed. My issue is what if multiple users attempt at the same time. I've had a snafu there before. x3
You pray
Having the host machine assign userdata objects to new players makes this way easier. X3
But ownership should be pretty reliable
And I get that. But I want 100% certainty
Ownership changes are not instant remotely either. So that would create a race condition.
Yeah, in my case here, we'd just be assigning each new player joining the world one of the possible WorldSize * 2 userdata objects, that retains our playerdata reference.
Thus we don't have uncertaintly
Well that is a whole lote more complicated than you might think. Without risking race conditions yet again that is.
I've made it work before. :P
The host machine receives a OnPlayerJoined() event, give the UserData to that player. Now I have a trackable solution running around.
A joining player would ever call that once, and upon leaving, we just empty out the userdata.
In my case here, I am almost 100% certain that this code beats the whole race condition
public override void OnPlayerJoined(VRCPlayerApi player)
{
if (Networking.LocalPlayer.isMaster)
{
// Let's give the new VRCPlayer a UserData to track them properly.
foreach (UserData user in UserData)
{
if (user.VRCPlayer == 0) // We found a UserData that is not currently assigned.
{
Networking.SetOwner(player, user.gameObject);
Debug.LogWarning($"Player {player.displayName} has joined and has been assigned UserData {user.name}");
return;
}
}
// No empty UserData was found. Sad face.
Debug.LogError($"Player {player.displayName} has joined, but no UserData can be assigned to them.");
}
// Non Room Master code goes here. Join messages and UI events.
}
Our UserData file contains the reference to the players VRCPlayerAPI index and t he PlayerData they would be assigned.
is it possible to see from who a custom network event was received in U#?
Not without a workaround, the last conversation here was about this #udon-networking message
Opening multiple clients does not work in the same way as being in a published world with friends, at least for me. I'm using the onDeserialisation Event to sync a mirror's active/inactive state for all players. When I test it in Build and Test with 2 clients, it works perfectly. But as soon as I upload online and have have a friend tell me if the mirror is synced, it's not. I thought the multiple clients should behave in the same way as actually having the world published... 🙂
always test with 3
one who first joins and thus becomes owner, a second person who interact and maybe or maybe not becomes owner, and the 3rd one should join later to see if its correctly synced for late joiners
you can simulate late joining by disableing the "Skip GO button" on map load and then clicking go much later, after the first 2 players have already interacted with it
@north mountainThanks, I just checked with 3 clients and the result was the same as when I tested it with 2 clients: the mirror updated for all clients. When I upload the world to VRChat's servers the mirror will not update for anyone else.
Has anyone else had issues with the clients not behaving in the same way as an published world? I'm so confused as to why it won't work when I build and publish
VPNs ftw
Hm - I haven't seen this issue other than with Stations not working correctly when using Build & Test. How are you syncing the change to the mirror?
Yeah, variable serialization/deserialization order is not guaranteed. If you absolutely need variables to be updated at the same time, you'll need to use a struct that can include them both, or an array. This is how the new Video Player Example works, keeping two floats in a Vector2 to ensure they are always updated together: https://docs.vrchat.com/docs/udon-video-sync-player#update-time-and-offset
I don't know exactly for vector3's, but the limit is around 60k bytes. If my math is correct, 60,000 / (32 * 3 / 8) is 5000
but it may be slightly off depending on how they get serialized. Not entirely sure
if you use onpostserialization, the serializationresult will give you the number of bytes which will be the most accurate way to know if you are approaching the limit
as for playerids, yes all players will agree on all playerids
Can you sync object[] ? Im assuming you cant, but I want to make sure.
Sad. So sometimes its impossible to ensure that certain data arrives togethre?
if it's manual sync I'm pretty sure they'll all arrive together
"variable serialization/deserialization order is not guaranteed." ?
Ohh I see order
But they should arrive together right?
that's probably because the example is using onvariablechanged, not ondeserialization
Yeah thats why I dont like that event lol, and other reasons
onvariablechanged will fire in a random order, ondeserialization fires after everything
Thank goodness that I decided to finish scrolling before replying with concern to momo lmao.
👏 magic
Yeah I had that issue too, but it makes sense once you understand how ownership works
The ownership request probably doesnt get handled in the same frame, so youre not owner yet when you request serialization. However youre right that that would be weird behaviour and probably not intentional...so I dunno either
Thanks so much it worked after many tries. I'm not sure why it didn't work the first time but it could have been because I was at 200 ping and didn't wait long enough for the mirror to update. Anyway it works now, thanks a lot! 😇
Bit of a stupid question, but what is the easiest way to achieve a synced vec3 array? I have an array the same length as the number of players, and for each index, one player is writing to it every frame, and every player is reading the entire array every frame. I dont think I can use the built in synchronization. Also these vec3 are a type of custom position I need.
because rather than using a 16 length array. I could just have 16 individual vec3 and that would definatly work, but its so akward. was wondering if theres a better way
the vector3 array isn't the problem, that's fine
literally just make an array and mark it synced
most likely the problem is instead that you are having multiple players trying to write to it at the same time. You need to transfer ownership first, then set it, then call RequestSerialization
this process takes a bit of time and if two players do it at the same time they will conflict
yeah, essentially I want each entry in the vec3 array to have adiffernt owner, which is not the default beheavior of checking synced
the only way to do that is to assign a completely different object per player
you cannot do this with a single object
didnt think so but wasnt 100% sure. thank you.
I'm just going to add 16 vec3 variables and a lot of if statements, it wont be pretty, but it will work
that's completely unrelated to your problem though
you can't have a different player own each variable
you have to have one unique owner for the entire object, that means the entire udonbehaviour and even multiple udonbehaviours if you have them on the same gameobject
does anyone know how to fix your inv and req inv, like I see my mic and emm notification but I dont see the invs and req invs
even when I open my menu
This channel is for questions about Udon Networking. That sounds like a support question. But if by "emm notification" you mean "emmvrc" the modded client, then you should know that that's breaking the terms of service and is not supported.
Most likely it is not compatible with the current version of vrchat and you will need to clear it out by following the steps in #vrchat-support
This channel is for questions about udon networking. That is a question for support. Please follow the steps in #vrchat-support
How does vrchat handle instance owners and object owners leaving an instance? Is a remaining player assigned as instance/object owner, is the owner ID left attached somehow, or does the instance/object just become ownerless? Maybe I'm blind but I'm not finding that info in the documentation
A new owner is selected
It will always transfer to the person who has been in the instance the longest
Got it thanks!
I don't know if it is possible to use VRC's object pool and locally have each player control it. It's having some weird unintended behaviours when it cycles through the first loop of objects
Actually, regarding TryToSpawn() from VRCObjectPool, does it also alter the position of the object in some way once that function runs?
I just want to send a simple string with an rpc, HOW
udon does not support sending variables with network events. If you want to set the string to a finite number of possibilities, you can just make a unique event for each possibility. If you need the string to be fully dynamic (or you don't want to bother with events) you will need to make it a synced variable
O god, well I need to sync the name of the player who interacted, so now I just change the owner when interacting and syncing the name up with the owner
Thanks for the quick response
What was the Udon sharp network code generator all about then?
the code generator is if you have a lot of possibilities, but it's still a finite number
like if you want to have 100 events or something
oooo Man I was looking at that github page for so fucking long just confused of how the fuck they send the variable lol
when you do SendCustomNetworkEvent, it just takes a string. You can construct strings dynamically, so you can do something like "event" + int to make event1, event2, event3, etc
I have a bit of a problem, I am trying to enable and disable some pickup objects with a button but that seems to only enable them for the instance owner or the local player, I am not sure which, but when someone enables them latter it spawns them at their spawn location and they are not networked at their correct position. How do I fix that problem?
Do I need to use on deserilization for this?
This is how the buttons spawn the objects at the moment. I did try the player api branch =false approach but that didn't spawn them at all as that boolen is allways set to 0
If you want to sync the position of an object, check out the VRCObjectSync component. You can see it working in this example: https://docs.vrchat.com/docs/udon-example-scene#pickupcube
I allready use the sync components on all the objects
will this work if they are disabled locally?
So say someone else enables the pickup objects
but you have yet to do so
I can't see what else is going on in that graph - are you actually calling SetActive on the GameObjects to change their active state?
If you want to make objects visible and invisible for other players, you will need to use a Synced Variable to change the active states instead of just changing it directly. ObjectSync does not modify the active state of objects, it just moves the Transforms and Rigidbodies
ohhhhhhhhhhhh
those variable settings that they didn't explain at the begining of udon
ok hold up let me see if that works
the gameobjects cannot be synced
is active bool can though
Will test that out now
yep, exactly. Whenever someone changes the bool, you can listen for the Variable Change and update the state of the object. Check out how it's done in the Udon Variable Sync examples: https://docs.vrchat.com/docs/udon-example-scene#udon-variable-sync
These on the documentation site?
I did see a download link for these though I will probe the examples as well that come with the sdk
actually nvm I just read the link
nope that didn't work
wait no it kinda worked?
nvm it did, thank you very much for the help momo
if I initialise a synced variable like [UdonSynced] bool test = true; and give it a default value true but someone sets it to false later will it get synced for late joiners as well or will it use the initial value?
okay thanks
It always starts as the initial value locally and then gets overriden.
Hey all, so does Manual Sync mean I have to manually tell the clients that the variable has been updated? I've got an udon synced array of ints that's populating properly on the host, but all clients are returning an array just filled with 0.
public class Example : UdonSharpBehaviour
{
[UdonSynched]
int playerIDs = new int [48];
VRCPlayerApi[] playersBase = new VRCPlayerApi[48];
public void FillArrays()
{
VRCPlayerApi.GetPlayers(playersBase);
for (int i = 0; i < playersBase.Length - 1; i++)
{
if (playersBase[i] != null)
{
playerIDs[i] = playersBase[i].playerId;
}
}
}
}
Here's my logic
So later in my code when I send a Network event with target All, that just prints the playerIDs array in each client's log, the host is correct, and each client just has 0s
whenever you change a synced variable, you need to have ownership and then call RequestSerialization
this is the meaning behind manual; by doing RequestSerialization you are telling it that there is new data to send to the other players
btw, you can learn more about networking and how the different parts work here: https://docs.vrchat.com/docs/udon-networking
Thank you as well! I was looking at that, but I knew I was just missing the function that made manual.. well.. manual.
Am I right in saying there's no simple way to send data along with an event?
Not currently no, but if you need to synchronize data you usually want to use synchronized variables anyway, because those are also synchronized to late joiners.
@lone zealot I've been working on collider impact sounds for props but of course, they only play locally. I'm looking to send events so they'll have some playback for non-owners, but seems I'll only be able to play them at a fixed volume rather than sending the volume data along.
The best approach to that is to sync the objects themselves and then detect collisions locally on each client
The problem is that doesn't work while the object is still under someone else's control. If a friend is holding an object and hitting it against the table, control is locked to them.
thats exactly how it works. As long as you can see that object moving in your friends control, playing the audio and having it heard by all near it is just about your play method. try using a simple collision check that sends a net event to play audio.Play(); works without a hitch for me every time. Granted the sound might get delayed by a tick or two.
That should hopefully be close enough. I mean, physics on objects once they've been released is very haphazard. It was curious. Throwing a object between myself in the UK and a friend in California on a EU origin server, we were mostly able to catch the item as catching an item favours the grabber as long as they successfully grab it in their simulation.
However something like just observing someone releasing an object and letting it drop to the floor, objects will often just hang in their released position for a while before suddenly getting a positional update, rather than letting local physics take over until another positional update is available.
The object certainly knows enough locally to know it should stop tracking with the other player's hand, that update comes through fine, but the fact it doesn't also switch back right away to letting gravity grab it is odd.
Just make impact send a network event that fires the collision audio and sets the volume from a synced value.
Isn't there timing issues there? What if the value sync message arrives after the event?
For the moment I've just set any audio generated by someone that wasn't you to be half volume since that seems like a good idea anyway.
how can i know/where can i see why an attempt to send serialized data has failed?
Try here, it explains what can be serialized and why serialization might behave odd: https://docs.unity3d.com/Manual/script-Serialization.html
hmm, so what i'm trying to serialize is serializable, i seem had to add SerializeField bc they're private variables (also ps. i'm using U#) but that didn't help, any other info there is a bit confusing (i guess all that is not that important in this context here)
unless devs' claim that string[] can be now serialized is false
You tried tracking the movement of the value with Debug.Log?
i'mma try that
ughh hold up
i saw something about null, couldn't it be the reason it fails to sync?
like my variable string[] will be allocated but i won't be using it fully immediatelly, as in i do new string[32] but i'm only using first 4 strings, the rest have null
also the data looking as expected
Yeah there is a problem with syncing null strings including in arrays.
yep, i've just tested it by putting "" in every string, now it does sync
ffs, that took me couple days to find out why my OnDeserialization wasn't getting called
For quest syncing, to clarify: I can remove every non-syncing script component under a gameobject as long as its name is identical in hierarchy between standard and quest versions?
Even say, different udon scripts, provided scripts on appropriate game objects, have identical [UdonSynced] variables?
removing components should be fine, yes
especially if they're not udonbehaviours
if they are udonbehaviours, I thiiiink yes but maybe no
so i should just keep empty udon behaviours that have a script, but serve no purpose without their underlying colliders?
probably a better test to keep them first, then remove them after if syncing does work.
I'm much more confident that keeping it would work
removing them is what I'm unsure about
Isn't there post late update in U#?
oh thanks
Continuing the conversation:
#user-support-old message
Would anyone know how I could make something like this (||https://developer.altvr.com/assets/videos/load-app.mp4||) but with the web panel?
vrchat doesn't have a web panel, that was removed years ago for security concerns
it only has a video player
Do we have any information on why a SendCustomNetworkEvent could fail? I'm currently executing it on a script where I am the owner, master and local for the script, yet it says it's blocked
does the function start with an underscore?
Nope
I made sure I didnt from what I read in the documentation
Its just getting randomly blocked ; ~;
at first I noticed it getting blocked by client, so I thought "okay, non-master, lets see if I can try as the master" but it also gets blocked, usually thought this happens when you aren't the owner, but each one is owner of their script
I'm not aware of any way that network events can be blocked aside from the underscore
in the open beta, "none sync" was just added which would prevent network events, but it won't cause that log message
and if you instantiate an udonbehaviour, networking won't function but again, I'm pretty sure it won't result in that log
Oh I saw that actually! Though, my intention is to send the event, not just to get rid of the blocked event
do other network events work or is it just this one?
I dont have any others that do sendcustomnetwork but... you did make me think of one thing
Let me try something real quick
Aha!!
Wow, this is weird, but okay sure I'll take it.
Turns out, because my scripts for every player (called Player_Slot0) were parented to a manager script
That kind of messed up the network calls? I'm surprised that messed up the master too, considering they are the owner to the manager of player_slots
So, (hopefully) simple question; I have an object with an animator using an integer value, and a couple simple scripted buttons to assign the animator int to a certain value depending on which button's pressed... if I wanted to make it into a synced integer so it works for late-joiners, would I need to do that on the buttons, or the object with the animator itself? edit: nvm, I'm an idiot who didn't originally think to check with discord search if someone else had already asked the same thing >.>
|| can hear tupper yelling somthing about forums in the distance ||
Hello, I am currently a novice at Udon and trying to understand some functionalities.
Currently, I want to make an interact function a global action for everyone in my world. I want to make it so that everyone can see a door open/close when I interact with a door handle. I saw that using a SendCustomNetworkEvent can work with this, but I am not sure how to implement it.
The following screenshot is how the Udon graph looks like right now:
The functionality itself does work, but it is currently local.
Ah, I just found out what went wrong. I should have made a custom event and connect it to where the SetBool arrow is at. Then, I should connect the Interact event to the SendCustomNetworkEvent.
Never thought I would live to see the day where I miss vBulletin
just to confirm: a pickup shouldn't need anything more than a VRCPickup and an Udon Behaviour in order to be synced, right?
ok wait, I just found a google result about 'vrc object sync' and that fixed it.
why did I think it needed an empty udon script..... did that change recently?
wait no yeah, I've got something else in the same room that just uses an empty udon script
Yeah that was sometime last year that the blank script/graph was used. Sync has been improved for a really long time.
ahh, OK, good to know, thank you!
@fossil kite - FYI there's lots of documentation about how Networking works here: https://docs.vrchat.com/docs/udon-networking
That video at the top explains exactly what you discovered - that the simplest synced object is a VRCPickup and a VRC Object Sync
How exactly is the new "None" synchronization option supposed to be used?
I don't recommend you use it yet. I'll update the documentation when it's updated to fix some issues.
Yeah, I tried applying it to an existing script and it seemed to break or disable it.
yea, I had a few scripts that are purely local with no networking whatsoever and it broke them outright.
Specifically atm
- Scripts with networking.none do not have start()/update() and can not recieve networked events
The first part is a bug though, so really they are kinda identical to local only scripts, unless there is some other use intended by VRC
I was trying to apply it to a script with interact() that sends a custom event to another script for the local player and it still broke the script.
Yeah, as far as I can tell currently all non-custom events are broken for None-synced objects. We'll have to wait for VRC to fix it before really making use of it (if you have UdonBehaviours that are purely for data storage or which only have events invoked via SendCustomEvent it may be usable for them though)
I do wonder how that was missed in testing...
set to none sync
syncing no longer works
pass
🤦
When you script I cant find a use for it unless its something special. If you want something local you just dont script for sync. period. Thats how I would miss it while testing, not having a reason for it.
the purpose is to reduce the number of components that are doing things. It has an impact on performance
yes we need maximum performance
i am doing extreme number of computations in my mirror world
i have turned my vrchat world into a cloud service hehe all your base are belong to me

Hi! I'm trying to have a light turn on after 3 seconds after pushing a button, I have made this Udon graph and it almost works because it does turn on after 3 seconds... But then it turns off again. And on again. And so on every 3 seconds, what can I do to make it light on only one time? Thanks!
You are looping an event every 3 second. Try connecting the custom event to the Set Active only, and remove the flow from the delayed custom event to the Set Active
Thanks, that worked like a charm!
Hewo, i'm trying to make an exit player trigger (for disable a mirror) but i'm using "onplayertriggerexit" it takes all players in the map and i would want it only local, can someone give me some helps about that ?
Like this, but with trigger enter as trigger exit https://cdn.discordapp.com/attachments/657394772603830360/884441181319217212/unknown.png
Perfect it was that "get is local" check needed, thanks a lot ! have a good day !
How does property synchronization work? I mean in the assembler it is possible to specify .sync vector3Variable->x, linear, will this only synchronize the value of x field of the variable? Or is this functionality not implemented?
the assembler does not have property sync, that's sugar added by the UdonSharp compiler
But i don't have it in my project...
I checked and re-imported vrcsdk again, it looks like built-in functionality
could you explain the problem you're trying to solve? Are you writing assembly code by hand?
I'm writing a compiler and decompiler for udon assembler, so i don’t understand what to do with this functionality
I would like to implement the full functionality if possible, so such moments are confused
Ah, I understand. I'm afraid we don't have documentation on how to implement sync at the assembly level yet. You can find our basic assembly info here: https://ask.vrchat.com/t/getting-started-with-udon-assembly/84
And you can look at UdonSharp, UdonPie code, etc to see how others have done it.
Looking to dig a little deeper? Want to try building VRChat Udon out of assembly, or even build your own compiler? Here’s some first steps you should look into. IMPORTANT NOTE: If you’re just getting started with Udon, this probably isn’t what you want! Check out our thread on Getting Started with Udon. How Udon Works Udon has many layers: a n...
looking at some assembly, it looks like you just need:
.sync symbolName interpolationType
where symbolName is a valid symbol (aka variable) that you define separately, and interpolationType is none, linear or smooth
They don't have any use cases either for this. But thanks for the tips
I have already implemented this functionality, i just noticed that there is another use case when viewing the assembler parser
Ah ok - I think I understand now. You're looking through the code and you see that it's possible to specify the property of a struct for .sync like vector3Variable->x to sync just the x value of the given variable, and you're wondering if it's been implemented or not because you haven't seen any compilers that make use of it?
Yes
If that's the case, then I'm sorry that I don't know if that particular syntax is fully implemented or supported, but you're welcome to give it a try and let me know!
Okay, thanks for the help anyway
Will using Update event for RequestSeriilization cause problem?
I'm trying to sync some array between owner and other player but it didn't seem to work
or Seriilization didn't work for arrays? even only one dimension?
It does
Don't serialize every frame though, do it only if the data changes.
Also do you make sure your player trying to serialize is the owner of the game object ?
If data storage game object is the object with the Udon behaviour, yes
it is
do I have to make the role variable and role_sync both 'sync', or I have to leave the local one not checked?
Hi any possible reason the OnDeserialization wouldn't get called?
I've set the UdonBehaviourSyncMode to BehaviourSyncMode.Manual. Besides I have the similar set up for another gameobject in the scene and it worked just fine. It is a empty object I made into a prefab with a UdonBehaviour component whose OnDeserialization is not working. I used VRCInstatiate to spawn the prefab as a gameobject in the scene.
Networking currently does not work on instantiated objects.
I see...
Thats what I suspected.
Is there any work arounds if i want to sync states on an instantiated object
is there a limit for function like getprogramvariable or sendcustomevent?
You would use object pooling instead.
is there a faster way to populate the object pool without having to drag and drop multiple times to the inspector menu.
Thanks for the answer btw
Don't think so
if onlyyou could shift select then drag them into the inspector panel that would be super handy
The Udon Editor can't handle that due to serialization issues I think.
I see .
Thanks for the answers!
Lock button in the top right corner of the inspector. Then sift select the objects and drag them onto the name of the array.
I dont think that works in UdonBevaviours. As I said the custom Editor cant handle it. But definitely try it out.
object pool isn't an udonbehaviour
Pretty sure it works in udonbehabiours
Actually My experience is with udonsharpbehabiours, not sure about udonbehaviours
Worked like a charm thanks!
can I use two ondeserialization nodes in same project but in two different udon program?
yes of course
ondeserialization happens when the object receives any synced data, so it would be quite limiting if you could only have one in the entire project
got why mine serilization didn't work...just used array.copy and array.copyto in a wrong way....
Well it doesn't work, throws an error Caught NotImplementedException: Reflection not implemented yet, not encoding __0_syncVec.y. It looks like this feature hasn't been implemented yet. Or maybe it's obsolete functionality i don't know for sure.
I can't sync int array....
so when player2 died, he will be set as owner of the Data cube, and set his status to 0(IsAlive[1]), which means he's dead, then request serialization
and on deserialization, player1 should be sync with the IsAlive data sent by player2, but it stays the same
Not regarding your problem, but I don't think you can use GetPlayerId as an index for synced arrays.
If i remeber correctly the player ids are different for different users. NetworkIds are the same, but cant use those directly either since they keep growing if player leave and join
GameID[] is an int array that store playerID when they join the game
I use some display to make sure player2 is set to be the owner and set the correct value for the array, but it didn't sync to player1
everything I put behind OnDeserialization just didn't work
Oh, i see. I would try adding some debug output to the on deserialization method. Check if it fired, if Totalplayercount is the right length, maybe even iterate over both arrays and look what's inside.
Try to narrow down where it fails
that's what I already did
I put a debug output just behind deserialization, before anything else
What exactly is data storage game object? Is that a variable you defined yourself ?
just a cube that I run the udon program
You should be able to do something to the equavalitent of this.gameobject
That way you can make sure you set the ownership for the object with the script
I also did that with Isower() if true ,set text to 'owner'
Did you confirm it sets the array correct on the owner side ?
yes
Also did you try plugging the gameoebject into the request serialization method ?
Yeah I know, but everything else looks correct, so that was my last guess.
Just to make sure cause it's the last thing I an think of, your udonbehaviour is set to manual sync ?
yes
OK I see, I need OnPreSerialization event
not DeSerialization event
However I use requestSerialization with OnDeserilization in other udon programs and they did work.....
....
PlayerIds are consistent across clients per instance.
ah yeah, i was wrong. Player ids are the players network ids.
how does one instantiate a prefab on all clients? like i know there's VRCInstantiate but it does it locally only
use an Object Pool instead: https://docs.vrchat.com/docs/udon-example-scene#objectpool
right - you'll want to decide the maximum number of objects you'll allow
this would be the case even with Instantiation
well it should be at least like couple of hundreds but it would be wrong to have couple of hundreds of prefabs spawning at the beginning of joining the world
they won't spawn when someone joins, they'll already be there, but set inactive
so rather than experiencing a hitch as an object is created, they do all the loading up front
but then like i have to put like hundred of these prefabs in the scene and manually put them into that pool array
correct. You can save some time by locking the inspector with the pool selected, then you can drag and drop those prefabs into the pool array.
yeah, uhm, i find that lame
your other option is to write an Editor Script to do it for you, that's how the PlayerDataManager in the Obstacle Course example does it without requiring the creator to drag objects into the pool by hand: https://docs.vrchat.com/docs/uoc-how-stuff-works#playerdatamanagerhttps://docs.vrchat.com/docs/uoc-how-stuff-works#playerdatamanager
ok nvm, i found another solution for my problem
oh cool, what's the solution?
the thing is, i also need the synchronize some data, and as with my testing, trying to do VRCInstantiate at the same time and do some syncing doesn't work, i thought to have one synced data, and when i change it, it sends the data to other clients, and on OnDeserialization i use VRCInstantiate and set the value locally.
but again, thinking of that, it won't do anything for the late joiners
right. You could sync an array of data to update things for the late joiners, but you'll have to either set the size of the array to the max number of items you'll have, or recreate it if it's expanded beyond its original size.
anyways for the context, i'm trying to make my own pens that synchronize the lines
That's somewhat like how the Udon Pens work
oh haha - what do you want it to do different from the existing pens?
to have a ui on pens instead of the triggers, like the QvPens has it
i already did it before, but i did it in classic way, just sending the events of starting and ending the drawing
you could just duplicate and modify the provided pen prefab or the fancier one from the VRChat Sketchbook Jam
that one uses synced arrays to update line points for late joiners
yeah, the thing is, i also want to understand how the whole thing works
but that's like array of array of Vector3?
and array of arrays is not (yet) available?
this doc explains the basic pen: https://docs.vrchat.com/docs/simple-pen-system
and this one explains some of the more complex ones included in the Sketchbook Jam Example: https://www.youtube.com/watch?v=LHwmlU3ZZTc&t=518s
Check out the example world for our first World Jam!
You can find more info and join in at https://itch.io/jam/vrchat-sketchbook-jam
0:00 Welcome
0:09 Theme Reveal
0:32 Trying each Drawing Tool
1:31 Build, Test, Publish
3:31 Changing the Lines
6:44 Changing the Color Picker Graphic
7:48 Changing the Models and Textures
8:11 Intro to Udon Progr...
not quite - each Line syncs its points as a single Vector3[] variable
I want to make a script that opens a door while players are standing in the trigger area and closes it when nobody is in the trigger area
I should also note that the doors in question are double doors and I want them to open by using animations
Have a script that increments a counter everytime OnPlayerTriggerEnter is called and decrements said counter when OnPlayerTriggerExit is called. Then do a check each time to see if the counter equals 0, and change the door state accordingly.
Do you have a visual aid I could look at?
Not on hand. I'm on mobile atm.
this tutorial walks through making a door, controlled by an animator, which reacts to player enter/exit. Should be a solid start for you: https://www.youtube.com/watch?v=95jRByYHE4Y&t=663s
These features are currently available only on our Open Beta version - check our Discord for more information.
Learn how to make doors that automatically open and close to let players through. This tutorial will use the Udon Graph to work with Player Triggers, Haptics (aka controller vibration) and controlling Animators with Udon.
Music i...
Hi, if A sent a custom network event to all, then B sent one immediately, how would others recv these events? is it guaranteed that msg A would be recv first by everyone?
one way to make sure everyone having the same result is sending a msg to the owner then the owner send it to others, but there will be one or more seconds' delay when using manual sync.😂
Manual sync is faster than custom network events.
There is no way to guarantee order of network events received from separate clients.
What are the rules for Instance and GameObject Ownership in Udon, particularly if an instance or GameObject owner leaves the world?
- How does ownership automatically transfer in this instance?
- Is OnOwnershipTransferred called in all cases regardless of whether you facilitate the ownership change or VRChat due to players leaving forces the change?
Ownership will always be transferred back to the master
Onownershiptransferred should happen in all cases, yes
What if the master leaves? (thanks btw!)
Then it will transfer to the new master. The master is always the person who has been in the instance the longest
Phew, the rules are nice and straightforward. Thanks ^-^
thanks, and do you know why does this take seconds ? ----- client A -> SendCustomEvent(target.Owner) -> owner B (Manual sync) -> change values -> RequestSerialization -> client A OnDeserialization
Mainly cause of general global network delay.
I think you guys might want to update the description as it still says features are only available on the open beta 
Also there are different priorities of various network activities.
Manual sync is on the highest priority. Custom network events are on the (for lack of better term) medium priority. Continuous sync is on the lowest priority.
So you have general network overhead and internet delay, plus the "medium" priority limitations of a custom network event, doubled cause of the round trip to pass data back.
It all adds up.
i tested this on one PC with 2 VRChat clients, there is still an obvious delay. 😂
and, if i change it like this. the delay gone.
A is client and B is owner (Manual sync)
client A -> become owner -> SendCustomNetworkEvent(target.Owner) -> client A (new owner) change values -> RequestSerialization -> client B OnDeserialization
thus, the delay comes from SendCustomNetworkEvent ?
yeah, custom network events are usually around 0.5 seconds
manual sync is about 0.2 seconds and continuous sync is minimum 2 seconds
[UdonSynced] bool _active = false;
public bool Active
{
get => _active;
set
{
if(_active == value)
return;
_active = value;
if(activeParamId != -1 && anim)
anim.SetBool(activeParamId, _active);
RequestSerialization();
foreach(var device in devices)
device.Active = value;
}
}
public override void Interact()
{
if(!Networking.IsOwner(gameObject))
Networking.SetOwner(Networking.LocalPlayer, gameObject);
Active = !_active;
}
public override void OnDeserialization()
{
Active = _active;
}
Is this going to result in an infinite loop of serialization and deserialization?
I'm using manual sync.
Not sure about RequestSerialization in there, but I want to sync with late joiners
I'm syncing a light switch (at the moment) and also need to sync everything it toggles so the device.Active property looks the same as this one
That should work, but Active = _active is not necessary. You should instead add [FieldChangeCallback(nameof(Active))] to _active
The requestserialization in the property isn't going to do anything if you're not the owner, so it'll work. But I think it will log an error or warning so you might as well check if you're the owner first
Ah, thank you
Is it worth caching Networking.LocalPlayer?
or should I just pass Networking.LocalPlayer every time
(since I guess it's cached in there)
caching saves you an extern
Getting it from networking is probably an extern. The extern itself isn't expensive, but it would have to be scanned by the blacklist which is. If it's stored as a variable it doesn't need to be scanned anymore
Still, it's a minor thing that wouldn't make any difference unless you're doing something dozens of times every frame
I'm not, but I'm thinking there might be lots of scripts in the scene
so every little (simple) bit wouldn't hurt I'd think
Thank you 
Not expecting lots of scripts at some point is bad programming 😛
Especially if you start the project intending to define your scope eventually, like me!
having lots of code with very little scripts might seem like a good thing but trust me it's not and you should never fall for that trap
It's a balance. Some might even call it a form of art.
it is... interesting
In fact it is. One heuristic I have is that a file is too big, if I have to scroll.
However that's only for "real" C#. Udon is...well quite different
having to scroll more than three times in a single script is a no from me
for some cases it's understandable
like if you were making some binary format reader
I create 1 file per variable
Perfection.
it is in the Manifesto for Agile Software Development With Udon
I'm making a state machine without inheritance
It's bound to be a big script 
Are there any attributes to tell the compiler how to handle certain method calls, like inline it?
No its not necessary since Udon is simply a VM. The difference would be a single JUMP instruction, which costs nothing at all compared to any extern.
Functions that are called in the same behaviour are jumped to, behaviours called via reference are called with SendCustomEvent
Ah, I see
So to optimize try to pack everything into a behaviour on an object instead of multiple on the same object talking to each other
Technically yes. Architectural thats bad for encapsulation / seperation of concerns. However in Udon, design principles kinda go overboard anyway.
Udon aka thicc spaghetti.
slow spaghetti
What happens if I have a few scripts running on the master only (like the server in most games) and the master leaves the world?
I know the next oldest person gets set as the master but the values get lost if I didn't sync everything right?
So does it mean I need to sync even values that the client isn't going to make use of in case they become master?
yes, that's correct
oof, I guess that makes sense
How would I go about having a circuit breaker that enables and disables power for a bunch of devices without referencing every single device on the circuit breaker
Rather thinking of referencing the breaker on the device
Is there a way to send a network event for all udonbehaviours of a type?
Or would I just need to shove all the devices into the breaker script
I want when a device gets turned on to tell the breaker that it's using some power and when too many devices are on it should trip, which is simple, but how I talk back to the devices to turn them all off
you could have an array of "connected devices" on the circuit breaker, and when a device is connected it will be added to the array. When it trips, the breaker will loop through all it's connected devices and tell them the power is out
That could work, but what about the other half of power draw then
Do I just reference the breaker on every device, and all devices on the breaker..?
the device has to reference the breaker, but it should be added to "connected devices" automatically
Oh so should it just add itself to the breaker's array on device's Start()
yeah
How would I do that without a list?
just have a fixed length
Do i just declare a big array and hope it's enough
a breaker only has so many slots anyway, right?
Yeah but not sure how many just yet
A little annoying you can't just GameObject.FindObjectsOfType<>()
You can still do stuff like GetComponentsInChildren<>. I'm assuming those devices will be synced somehow? Then it's probably a good idea to keep them at the same parent anyway.
I haven't really decided but that's a good idea