#udon-networking
1 messages ยท Page 9 of 1
When player interacted before, its saved.
Then what happen is, the event is then propagate to other players via RequestSerialization.
Then the player locally will check if "isBoxTriggered" is enabled or not.
This also works if people Just joined
What ever i say here if its wrong pls do correct me.
Together we learn in progress.
bruh.. Just bruh. my eye blind.
Bruh theres issues.. ๐ฆ
it seem slike no need "On playerjoin"
i still cant fix the issue..
First player toggle the hidden box
Second player still can see the box.
Why.
I am very confused.
Can anyone explain why this dont work?
This shit is getting out of hand for me.
@humble girder
wha, its no bug, disabled object doesnt run a script on it
Okay so i checked.. it seems like its not sending infomation to other user / player.
why is that so?
@tulip sphinx but then why I can transfer the ownership to another player of a disabled object, but not back and forth? It doesn't make sense to me
in the video I sent, player 2 recieves the ownership, but he can't give it back
So it seems like the stuff is not send to other users.. in the world.. why is that so?
I test it.. 3 player in the world.
first player interact the hidden box.
The other player dint recieve any notification or any updates..
why is that so?
hmm
isn't it because you didn't change any value?
isBoxTriggered sendChange isn't ticked
it won't send any changes to other players
also SetOwner and RequestSerialization isn't a good thing doing it both at once, you can't be the owner immediately then sync all the variables, first you have to recieve the ownership transfer
give me in step by step.
so im not too sure, but maybe if you don't change any of your synced values, then I think the OnDeserialization won't run for others
Well i did.
Please do explain this.
it has to be ticked, if you want to send out changes for other players
is it still not working?
nope.
isBoxTriggered Change, you need to put it the "bool newValue" to SetActive()
like this?
but why.
When the isBoxTriggered is true.. why do u need the "bool newValue" LOL
then put it on the unaryNegation
but you want to use that new value
that comes from there
there are also a lots of example udon graphs of networking in your Packages/Samples/UdonExampleScene/UdonProgramSources folder that you can use to learn
so.. here the thing
some of them actually useful, like button sync, slider sync, toggle sync
In this picture when u interact, u set box triggered to true.
When box is set to true
It will do the things it needs to do.
While at the mean time, the box is set as Owner and request Serialization for other player.
so you wonder, why you need Set IsBoxTriggered and RequestSerialization too to send the data?
no i am just trying to make myself clear if i am doing anything wrong..
Explaining my understanding.
but it wouldn't run if another player would like to interact with the box for the first time, because he isn't the owner
that player would need to press it twice
so what is wrong.. what can i do to understand? to make it right?
how do i fix it?
All i need is a simple, 1 person press button, 1 thing gets hidden, 1 thing get shown, to everyone.. even those who join late.. is so Damn hard.
dont leave me hanging.
i tried setting if its not "Owner" then do the shit..
but it just dont work.
yes, because it does matter who calls it, if a non owner interacts with it, its getting the OnClick called twice, once in the non owner, and once in the owner too
it's my graph, you can use it
test it first, and see if it fills your needs
cause this is just 1 part.
hmm is it the same as the one u send?
i would rather follow via screenshot
that i would be able to understand how the logic flow.
but thanks once again.
been spending like a day for this.
erm lets says if any Toggle Bool change, this "Toggled Bool Change Event" will update or play right?
So.. basically
they are both the same thing, but OnDeserialization, you recieve every synced value there, but if you prefer to use "value Change", you can do it that way too
i dont need this 2..
Oh..
So this is my current Graph.
this red box.. i dont think need right?
you still need it, if you would remove it, it wouldn't update for the owner
but it would update for everyone else
hmm then i can just.. simply.. Event it?
you can do it that way too
So when isBoxTriggered Changed > play custom event.
ok.
hmm
i tried.. and its still not working.
can you show your graph?
Same issue, i have 3 instances.
Player 1 touch the hidden box.
Hidden box disappear and key appear for player 1 only
Player 2 and 3 still unable to see the key
can you try replace the isBoxTriggered Change with OnDeserialization?
yep
3 mins to build so gotta wait.
3 mins? but you can try it in the editor too
nah if i try on editor. i cannot control player 2 / 3 and check their response.
oh, actually true, i forgot
Oh shit bruh.. player 2 / 3 clicked on the box
straight crash.
The issues goes beyond what i am trying to fix here..
i did a oppsy..
this is a game, spot the mistake i did.. if u see it.. you will face plam.
While at the mean time i cook another 3 instance.. try figure out what happen XD its really funny.
you didn't call HideShowObject on the owner?
but that still wouldn't lead to a crash
this whole chunk should be deleted.
Alright youre right..
its crash so.. whats the issue. hmm.
okay simple .. instead of..
i will just use "constant bool"
lets get cookin.
so.. i have no idea whats happening.
so its still crash.
"COULD THIS" be the issue?
For me to know for me to find out.
lets get cookin.
it just makes the non-owner execute the code on the owner
it's needed
you could try this too
plus you had it target: "All" on that picture, it needs to be Owner
in SendCustomNetworkEvent
Does vrchat have pure server side scripting?
you aren't changing it for the owner of the object
so this "Owner" is wrong. ok..
it's fine, but you didn't have it on owner before
i tried just now also crash.. now i trying "All" with the new updates.
lets see
i am curious.
why and how does it just crash
without any error
what makes the game just "Crash" like that?
bruh now its not crashing. yes
but funny..
its like swapped.. lmfao
For example a player interact the box, the key is showned.
the second player doesnot see the key,
and when they interact the box, the first player keys is hidden
bruh.
i think the issues lies on this part
When cusom event play, it will check if the ower is pressing or not.. if lets say the owner plays..
it will go to true, and will set "IsBox Triggered" bool to True and then RequestSerialization.'
While at the mean time when the box triggered bool is set to true. The box changed gets things should be toggled on and off .
And only after if its settled, then its Request Serialization.
so it works, but not for the owner?
i think its like doing 2 things twice..
its like the button is being flipped.
then change the initial value of the bool, plus you haven't used SetActive after Set IsBoxTriggered for the owner
he won't see anything change
i try like this
The OnDeserialization only runs for non owners
it doesn't gets called for the owner
or value change too, they only run on non owners
okay crash again
hmm
what will happen if i move this to there.
it will check if its owner, if its owner then do everything.
hmmmmmm
oh yah but if itsn ot owner nothing will happen this means new joiner touch nothing will happen.
Okay seems like it crash again
Well i guess i messed up again.. i think this is the issue.. try spot it.
Could this be the "ANSWER"
it dint crash.
It seems to work..
Everything and everyone seems to see what its suppose to see..
okay so what happen is once i clicked.. this happen.. yep everything works.. but its sending SO many network
i think i know
but if i think about it, it's maybe wouldn't be able to work, if you are trying to disable the gameobject that should also send notification to another object too to enable itself
if the object is disabled, i think it cannot do that
but you could disable the meshrenderer, not the entire game object
thay way, you wouldn't stop your scripts running
btw, it turns out the "value Change" event happens on both the owner and non owners too, i was wrong about that
only the OnDeserialization runs on the non owners
this approach actually works, but i only disabled the meshrenderer, you would still need to disable the collider and interact text
Is it what are you trying to do?
Yes, but u need to check for this .. instead of player 1 toggle, player 2 toggle
you can just put the script on a separate object
player 3 will restart game and check.
@cold laurel but @rose rain wants to interact with that same object too, then disable it and then that script would enable another object
@rose rain it probably works, if the non owner can change it, then it's more likely the owner can do it too

btw, how can I get the component of the gameobject? in the bottom, that wouldn't work
do you just want buttons that disable themselves and enable something else and is synced
is that all?
yeah, basically
ait, gimme 5 minutes
is it just supposed to be a one-time use??
i think that's what @rose rain wants, he opens a box, and gets a key from it, then the box should disappear as my understanding
yes.
Here's the example
as you can see, I just set the active state depending on the trigger
if it's true then the button is disabled and the key is enabled
if it's false it's the other way around
the button is just another graph that looks like this
it needs no sync
so it can be disabled
well, it only took 6 hours to make an object disappear and another appear xD
but that 3 min build time is pretty long, I can test my changes in less than 30 seconds
well. this is the first part.
Theres the second part.
Touching the key with the lock, and then hiding both of them
I am pretty sure its gonna be copy and paste..
which i will do that later
gonna rest.
i think that will be much easier than this
You set the networkevent target to All 
this will cause a massive feedback loop :D
please set the target to Owner
Heres the Code again but with instructions.. correct me if i am wrong.
im not sure if it's safe from late joiners, you don't plug the "bool newValue" nowhere
try open the box first, then join with another player and see if it's synced correctly
Well i checked again its bugged. it seems like its not working.
its not working for late joiners?
no not working even when in game.
i loaded 3 instances.
second player press the cube
1 and 3 player dont see the key.
then i tried player 1 pess the cube, player 2 and 3 cant see the second key.
i think this part i need to Unary Negation
gonna do one final test before i go to bed.
do your key have the VRC Object Sync component?
but maybe it's not that necessary
i have that, but i change the thingy to Manual.. which does not allow Object sync
so i removed it.
hmm we need call the expert..
also i gonna go bed.
mine script was working well
kitkat is showed you a script too, why don't try those?
alright, have a good sleep then
Oh will try that later then
https://clientsim.docs.vrchat.com/ i think this may help?
ClientSim in Unity Editor
but can i do all this in just 1 udon behavior?
i tried this and it dont work.. with sync on..
i now trying it with sync off.
I tried and it does not work.. gonna try your method.
Alright its still bugged.
it does work for every case, for non-owner, for owner, for late joiners
you just need to assign the mesh renderer and collider to your script for the box
gonna try CyanTrigger
guess i found the issue.
This 1 sentence.. solved this entire issue.
that's what my code does, instead of disabling the object, it disables everything else, mesh renderer, collider and interaction
that's why it works
yesterday i mentioned it
i also had to found it in a hard way, like wasting several hours just to notice I can't transfer ownership of a disabled gameobject too
No Iโm tryna to figure out if you can run scripts directly on the server similar to horizon worlds.
Nope. The client will run the Udon scripts provided by the uploader
that uploader is the person who uploaded the world
Nope. VRC is pseudo p2p.
Thank you so much.
yeah instead of disabling the mesh renderer, collider.. i just move it out of sight.. that works..
i am using cyan trigger..
Hey guys, I am using a gun script to call in a game object from an object pool, then play an aniamtion and return it to the object pool.
however i am encountering an issue.
there is 3 beacons in the pool. when i spawn all 3 beacons they spawn in correctly and they return correctly. the animation works correctly. when i try to spawn in a beacon the 2nd time for either 3 of the beacons it will be sent back to the object pool immediately. then when i try to spawn each beacon for the 3rd time it works perfectly as intended just like when i spawned it the first time. then the cycle repeats, when i try to spawn for the 4th time it doesnt work, then the 5th time it works. you see what im saying?
I think im having issues with the animation reset somehow not resetting correctly.
anyone got any ideas?
ideally i want to completely reset the state of the object so i can reuse it
it's maybe not the problem, but you don't need to call SetActive in pool objects. by default, they all turned off, when you try to spawn them they gets turned on, when you return them, they gets turned off
You are using it wrong "objectPool.Return(this.gameObject);" you disable the pool, not the gameObjects in the pool
I can't answer the question as to why something isn't working BUT I can offer some advice that might help remove 10% of the code. You are testing for null on objects that cannot be null and testing twice in some cases. If for instance the beaconAnimator is null you world is simply not going to operate. You could test it once in Start if you really, really wanted to but again the fix isn't to skip over code it is to "fail fast" so you can assign it in the editor and get it working. You test if localPlayer is not null twice. You test if objectPool is null. What happened that most of the objects you rely on have not been assigned? You don't things to continue as if there is no problem.
Thank you for the feedback @stone badger & @obsidian python
I'll see what I can change
Is there a limit how many networked object can I have in a scene? is 10000 is a lot? they wouldn't be all active at the same time
VRChat does this funny thing where once an object with a synced UdonBehaviour on it has been enabled for the first time it will still hog your performance even when you disable it again.
Also, yes 10000 is an extreme amount
another approach would be to instantiate 1000 objects in the fly, then I probably wouldn't need transfer too much data to sync them if I only want to just toggle them on and off, but idk how efficient would be that, instantiate is a very costly operation as i know
Instantiate is fine tbh
As long as it's used wisely.
tl;dr
whatd be a proper way to send it tho, mod 2 over x ints?
arrays are still not syncable right?
Arrays are very syncable
https://creators.vrchat.com/worlds/udon/networking/#requestserialization
You can sync variables and arrays of variables of the following types: bool, char, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, Vector2, Vector3, Vector4, Quaternion, string, VRCUrl, Color and Color32.
I'm thinking about a game btw that has multiple minigames in it, like fall guys and crab game where the blocks falls off under the players foot if they stand on it and other minigames, where you need to run around to paint the cubes below you, and others can paint yours too, and the player who has the most painted blocks wins, so they would need a lot of gameobjects, idk why noone recreated this in vrchat, or i have no idea if there's anything similar in the game
You wouldn't want every single object to have a synced script on it.
theres a game where theres 3 layers of honeycombs and you throw balls under other players while trying to stay yourself. so like fallguys level but with players breaking stuff themselves
i know, i would only need 1 master object who controls them all
also ofc there vrware with simplified version
but it's only one minigame in the game isn't it?
i wondered if i could have 10 minigames in my game that's rotating randomly
yes
@cold laurel alright, thanks for the help, maybe it will take a month or two to make it happen, and i think, it would be a cool game too
how can i make this global?
it just cycles between gameobjects that are different pool prefabs
but if it isn't global, both people would need to make sure they're on the same style of table to play, so i want it to change for everyone
Use a synced variable for your int and when someone Click they become the owner of that gameobject, set the variable and call RequestSerialization. Then in OnDeserialization you can have your OnClick method. So everyone will call the same switch
whats the sorting of getplayers()
As far as I know there it doesn't have a set order that can be relied on.
ok so that why i had my own
should still work pretty sure
wait does that mean getplayers is pure random?
or should i mix players myself as its not random enough
probably while noone joins/leaves it will be the same order each time?
i create new player array each time
unless its server will be passing me same sorting
Does anyone have an infographic of the Udon networking loop? Something people can glance at to reference?
The hardest part I find about networking in Udon is remembering all the steps or constraints. I know the concept of serialization and deserialization, but I want to have a visual way to reference it, instead of having to re-read the docs and formulate it in my head.
I will probably make one myself if there is not one that actually exists.
If you do could you please post it
which one is causing the error and how tf do i fix
That error comes includedโข๏ธ with the VRChat SDK
If somewhere DestroyImmediate is called in your code, replace it with Destroy instead. Though since it doesn't look like it, some logic might have to be pushed to the next tick for anywhere in callbacks the error specifies
Of course, not much can be done if its an sdk thing, just for you
Great thank you
hey hey, hopefully quick question about object pools and networking
Have a button trigger to spawn objects from a pool, standard VRC ObjectPool component, nothing fancy. But since updating to Unity 2022, late joiners aren't able to see any spawned objects from before they join the world.
Just wondering how to remedy it, or even just to get a better understanding of what changed to cause the desync on the newer unity. The world is almost a year old and never had this issue until the move.
Is sync OnPlayerJoined handled by the pool (should it be?)
Is the object pool object disabled by any chance?
Synced objects can't sync while disabled.
the pool itself is always active, the contained objects are disabled automatically at runtime (i assume by the object pool?)
That sounds normal
can you post the scrit on the button that spawns objects?
here it is
Has an extra bit to define the location of the object once spawned based on a separate game object
That looks fine too 
I be struggling, like I said it's been fine, even an older build of the same world with nothing changed is fine. it's only the 2022 build (of the same scenes with nothing altered) that has the late join desync issue ;-;
and it's fine for players that are already in the instance?
does it get fixed for late-joiners when you spawn additional objects?
it is, totally fine for everyone in the instance, if someone joins late and an object is spawned after they join its fine too
if another object is spawned, sometimes the desynced one will appear, but will be in a different location and will not be interactable until the person who spawned it picks it up
Gonna open VRC to get a more definitive answer, will update in a few mins
okay, so I redid all the tests, it looks like the spawner isn't the issue, and not a late join issue either
More that the objects themselves aren't syncing once spawned. If a player spawns an object, its only visible to them until they pick it up, then other players can see it.
I'm thinking it has something to do with the object sync method, being manual instead of continuous o.o
But they're stuck in manual, can't change it, the option is greyed out
How are you syncing the position of your objects?
The spawner doesn't have the option. Spawning isn't a networked event, but once the object is called, the spawned objects themselves are VRC pickups with object sync. (Recently switched to MMMAellons smart object sync) and that carried it before.
It seems that it only syncs once interacted with. Which i guess is in line with how manual sync method works on the object sync component.
I'm just trying to wrap my head around what changed between unity versions honestly.
But yeah no networked event nodes on the spawn button itself ._.
Does anyone know why OnDeserialization is not called?
public void Init()
{
//Make sure the master is owner of that object
if (!localPlayer.IsOwner(gameObject))
Networking.SetOwner(localPlayer, gameObject);
GetPlayers();
if (players.Length > 1)
{
RequestSerialization();
Debug.Log("PING?");
}
//Repeat the request
SendCustomEventDelayedSeconds(nameof(Init), refreshRate);
}
public override void OnDeserialization()
{
Debug.Log("PONG!");
var playerCount = VRCPlayerApi.GetPlayerCount();
players = new VRCPlayerApi[playerCount];
for (int i = 0; i < playerCount; i++)
{
players[i] = VRCPlayerApi.GetPlayerById(playersID[i]);
if (localPlayer == players[i])
{
fPSProxies[i].FetchLocalFPS();
}
}
}
its been happening on 2 scripts what just stopped all of suddenly and I can't find where is the problem, sometime if I remove and re-add the udonsharp script in the gameobject it magically starts working again but it's inconsistant
What is this line
[Behaviour] RPC SanityCheck/TargetPlayer on SceneEventHandlerAndInstantiator 50e506e7-e632-4720-a134-0325e31c98b3 blocked for Fairplex (local master owner)
Is it what is blocking my RequestSerialization?
I have a local UI they will choose from one of the answers and if its wrong I'll update this UdonSync variable isWrong. How could I do this?
public class PlayerData : UdonSharpBehaviour
{
[UdonSynced] public bool isWrong;
}
I found the issue, it is because one of my UdonSynced arrays was empty, not sure if this is a bug or what but I think vrchat should provide some feedback when a serialization fails
You must have some value to Initialize a synced array.
Yea I've been told that UdonSynced arrays shouldn't be null so I just initialize them with a length of 0 ๐คฃ
I have Player A and Player B.
A is currently Master and owner of some Object that has a synced integer i = 0, on Manual Sync.
If I do:
Networking.SetOwner(Object, B);
Object.i = 5;
Object.RequestSerialization();
Networking.SetOwner(Object, A);
Will that work? Im thinking it wont, but dont have the capacity to fully test this and maybe someone already has...
I've heard it does work, though if it's an option (not time sensitive), I'd listen for the OnOwnershipTransfer and set variables in there like recommended.
Though I'd avoid a setup like that all I possibly could and just have each player own an Object
The hearing it works is just hearsay though
Yeah I thought about it again and opted to just not retransfer ownership back. It doesnt really matter who owns it.
I guess another option would be if your value has a finite number of values it could possibly have, could have clients call methods to have the owner set the value
This won't work.
Later on when it tries to serialize (if it even gets that far) it will realize you're no longer the owner and just skip it.
"Ownership" isn't really a great name in this case. It is fundamentally "who gets to set the values such that they are serialized to all other players". It is not unlike raising one's hand to speak in a meeting. You get the "floor" but you don't own the floor. Change object ownership whenever you need to. I mean before setting values and don't worry about setting ownership back.
Yeah I was aware. Was kinda hoping VRChat does some weird internal magic that stores who is owner at which point when wanting to change variables...but yeah my hope was slim anyway.
But as I said, Im now doing the "right" thing now, so it doesnt matter to me anymore.
E
is there a way to detect if the instance is a private or public one?
Afaik no. Most worlds I've seen just prompt people when they join and ask what the instance type is.
big rip
Instantiation
Instantiation is currently (2020-06-17) not networked. Instantiated objects cannot be correctly synchronized. The only workaround is to use object-pooling. (Note: Please write a more detailed guide on that)
has this changed? if not can someone explain object pooling?
https://creators.vrchat.com/worlds/udon/networking/network-components/
VRC Object Pool provides a lightweight method of managing an array of game objects. The pool will manage and synchronize the active state of each object it holds.
To make an object active, the Owner of the pool triggers the TryToSpawn node, which will return the object that was made active, or a null object if none are available. Objects may be returned to the pool by the pool's owner, and automatically disabled, via the Return node.
Late joiners will have the objects automatically made active or inactive where appropriate.
This doc covers Networking Components, Properties and Events you can use in your Udon Programs.
Basically it manages synchronizing collections of objects to all players, including their creation/destruction.
oh thanks!!
instantiating itself wont be 'fixed' since its not broken, just every network object needs to have an id on upload. pool is the most common way around - but it still needs all the objects to be already present in the scene, but you can also create your own sync from the outside (some invisible networked cube handling sync of X instantiated objects)
Any clues on how to combat quest's sleep mode? It's kinda annoying how they can just take off their headset and essentially block some crucial objects from changing ownership.
Like imagine them just blocking your object pool so any new player doesn't even get an object assigned so they can't even use your world.
a bit of a hacky solution is only let pc users have prolonged ownership, and have quest users take ownership, change values, and send it back to the original owner
or you could have the owner of something send a networked event "pinging" every few seconds and if it stops responding have another user take ownership
prevent crucial objects from changing ownership
you mean if a quest in sleep mode owns something nothing else can take ownership? oh
the 2nd thing wont work then
Hmm, I could try looking out for a PC player to give them the pool management. However, that would only lower the chances slightly. They could still go into sleep before finding that PC player and then just lock the instance again.
they could still go to sleep before finding the pc player
quests take a second after being set down before going to sleep, really hacky but you could watch head velocity?
Uh, idk, but doesn't sound reliable
Is that a thing that can be done?
are you using udon graph or sharp
Sharp, but I do know how to actually code it. I'm just questioning whether watching the velocity would help me detect the sleep mode
wait nvm, i swear i found something on velocity but cant find it
if you have a pc player with ownership at all times, and the ownership is only transferred on user interaction itd be a bit before the quest goes to sleep
I can't guarantee such a thing, unfortunately. We're talking about random public lobbies here.
I know of no case where somebody falling asleep in VRC prevents thing from operating. What have you seen or what scenario are you imagining?
So I'm using cyans object pool fyi. Usually it would initialize and then fire a "ready" event to other scripts. This ready event is never fired because a quest player is in sleep mode while also being the owner of the pool. When I kick them, the pool instantly initializes and everything works fine.
I'll look into how the pool works later, maybe it's because of how it does things? But I did try adding a button that takes pool ownership while those quest players were in sleep mode. I saw my debug message, but the usual "setting owner" messages did not appear.
Sounds like something you could mention in the Cyan Discord to see if a) they are aware of it and b) have a solution if it is in fact a problem.
Oh yeah, I think I'm in it. I'll give that a shot too, thanks.
what proper way of sharing info to other players without giving each player its own object or putting 50 same functions in a script
i see its not possible ok
Hey guys. I need some help: can I make instantiated game object synced and global without using object pool? I have like 850 game objects that I want people have access to, but making everyone their own object pool with 850 game objects is... well... not a good idea.
you cant, every networked object requires a network id, but these cannot be created at runtime, you will need to rely on object pools for networked instantiated game objects and potentially come up with your own way of syncing them afaik
okay, thank you very much
you can write your own sync that instantiates objects and places them wherever you need while running somewhere else
oh, really? That's awesome! Any tips?
@delicate bridge not really, never done it and it supposed to depend on your goal. but basically instantiate object and store it in array so you can find it later, store it position in a synced array. for late joiners spawn as many as you need and assign the position on each one, same for when theres a change
Anyone got objectpool experience with udonsharp? Im having an extremely peculiar bug with it.
Client A, the first user, Joins the word. The object spawns and is attatched to them.
Client B, the second user, Joins the world. He can see the object on Client A, but Client B's object spawned for only a moment (according to the logs) and then was destroyed through some unknown secondary route.
Client A can still see the object on Client B.
If Client C joins, Client C can see Client A and Client B's object. Client A and B can see everyones object, including their own.
This makes literally no sense to me
Hey, I just wanted to spread awareness of an annoying but somewhat sneaky bug: https://feedback.vrchat.com/udon/p/master-sleeping-quest-potential-networking-issues-and-they-can-sleep-for-more-th
Basically, quest can go into sleep mode for a long time! Imagine someone just turning off their headset, not realizing they are still in the game for hours. Now imagine the master doing that and blocking all network traffic.
This affects Omegle VR which uses Cyan's Object pool to do most of it's work. And guess what, it relies on the master to handle its objects.
I also visited No Time Two Talk and it seems to have the same issue. I'm 90% sure @sharp solstice is the creator of that and might want to know about this. Sorry if the ping isn't relevant or I'm mistaken, but hope it's okay since you're part of VRChat afaik.
im having the same issues for my world too, this issue has been around for a while unfortunately
although im not sure if fax is having this exact issue, but there is a possibility
I haven't been using my world that much recently, but I'm pretty sure it was fine most of the time.
Like, look at how excessive it can get. Something is definitely weird (All of these are android users sleeping):
https://canny.io/images/68c47787e32eaac134ed925714037deb.png
Kicking them instantly solves it btw
ohh that looks really bad i didnt know it got this bad
im sure back in unity 2019, the sleep mode would take up to 5 minutes(?)
It's 100% longer now, to the point that there might not even be a cap.
i had this bug report earlier too, vrchat update must have broke it
fax had his issue occur around 1st December afaik
Just a random thought, but could it be mobile related? No idea how it handles sleep mode since I haven't touched that yet.
i dont know honestly
Well, maybe I should try it (even though I don't want to download apps ๐ด)
But in the meantime, I hope VRChat sees the canny and can do something about the issue.
In terms of VRCObjectPool itself, I found it helped to set the owner of the spawned object as it comes out of the pool. For a situation like the one you describe where you are looking for one object per client, I've had good luck with CyanPlayerObjectPool: https://github.com/CyanLaser/CyanPlayerObjectPool.
should i have posted this here? #udon-general message i am trying to make a zombie that comes to you and you have to fight it with a catana but... i have no idea how to make the zombie move
Hello everyone, im having this error which i believe it implies that im not the owner of the object im trying to request serialization for, which i dont understand because i have a networking.setowner right before on the same function, any idea of how to fix it?
Use the depricated nav mesh system, add a nav agent to your zombie and set the agents destiation in your code
Yea I ended up moving over to that in the end.
I still wanted to put holes in my wall because of Cyan's lack of good docs but... it works as expected now at least.
hello im trying to use Github to have a text URL that i can use in my world
ive read that i have to turn my repo to a page , and now that ive turned it into a page
and clicked
https://MyUsername.github.io/Test/
it just brings me into a
you shouldn't use github as a file hosting service but if you really need to, use raw githubcontent
why not
and what should i use ?
also i want me and my partner to edit a file , cause we share the spinner table and other things that w both want to edit on
ive tried the https://raw.githubusercontent.com/myusername/myrepo/main/Test.txt
but for some reason it just wont work
Its against GitHub's TOS
Its probably in VRC's blacklist
you know what i could use ?
cause pastebin doesnt allow NSFW stuff ,
I host my own website so I wouldn't know
do you use something like wix to get urls for your projects xd?
No like I actually coded my own website
so you have a server ?
raw.githubusercontent.com is not in the allowlist
the string loading allowlist is currently:
*.github.io
pastebin.com
gist.githubusercontent.com
api.fynn.ai
api-dev.fynn.ai
*.xr-marketplace.com
*.v-market.work
*.vket.chat
*.poly.jp
*.vazar.jp
*.catsudon.net
*.vazarapi.com
that is so helpful thank you so much !
the only issue now is this freakin page XD !!
when i go in settings > Pages > Your site is live at (my link)
nvm i figured it out , i had to type the file name in the url ๐
sucks that i have to do it that way , ;P
should be easy to have a button on a github that makes that url
What are you trying to solve?
i basicly want to use github to load url text file ,
i used to use Pastebin Unlisted url
but ive gotten githubs link to work n everything , the only issue i have now , is that my repo has to be public , so anyone can yoink my text files through my profile
Every resource on the internet must be public to begin with.
Even pastebin is public. It's just unlisted as in not being publicly announced its existent in pastebin website.
yeh thats what i want , but im not sure how that works ,
and what iwant is to not be shown on my profile this repo ,
only people with the link can see it ,
not sure that's possible
using a gist might be closer to what you want? they're not as prominent on your profile as a normal repo
actually i forgot, you can make them kinda "unlisted" like you want
tell me more ? ๐ฎ
Hey, everyone. Can I create my own class that can sync? I tried this, but U# compiler says "Cannot sync type 'FitBox.PunchClass[]'"
tf with screenshots
You have to store and sync enum as int variable instead.
is this the only way?..
There is also a few more complicated ways which make it more difficult than necessary.
You can't sync references to objects
k, thx
i don't believe that's true. U# should handle that automatically making syncing enums transparent to the user
Yes I actually misread the code and didn't see the OP was trying to sync an object.
Enums in U# are replaced by ints at compile time btw. Enums dont exist in Udon, so they are merely a compiler feature.
poke
I think they are referring to the literal gist.github.com website.
This is wrong.
Understood
https://gist.github.com/
create gist button can be selected between secret or public
secret can still be used by direct link
ive tried gist , but i cant collab with it and it creates a new link everytime i update it
@obtuse echo
there is some way you can get a persistent link, i can't remember how but i did it for YTS
as far as collab, yeah fair point
Pay for GitHub, then you can have private repos which deploy Pages sites using a custom domain. Then nothing ties your account to the URL people use to download the files unless GitHub complies with a court order
If you use an "apex domain" setup for GitHub Pages you make an A record to their servers not a CNAME to your .github.io
So it's fairly stealthy, but not foolproof obviously
@wooden tulip ^
hmm , i might pay for it , how would i set it up tho , do you know ?
Register domain name, set up DNS per this guide, then change your custom domain to the one you registered in GitHub repo settings for Pages, then you should be done
https://yoursite.com/yourfile will serve up /yourfile from your repo branch (or whatever is being deployed to Pages based on your config)
aah ok , this is super helpful, i appreciate it a lot thank you so much !
On the GitHub side this is what it will look like
Note the Custom Domain field
Just to be clear, whatever you're working on is NSFW but not illegal, right?
I'm all for helping someone hide their furlust from their friends, but if this is something other than that you may want to seriously reconsider hosting it on GitHub, review their TOS to be sure you're not violating it by posting violent or extreme content, I believe they have a few rules there
Isn't using github.io domain a sole purpose for being in vrchat whitelist? Otherwise you could use amazon S3 to host files.
lol nothing illegal , just a truth or dare /spin the wheel with NSFW questions
If you don't want to be anonymous then yes ofc username.github.io is easiest setup - if they want to be anonymous then they'd want the custom domain - sorry, not sure what the goal is, my understanding based on reading the convo above was the latter, if I am wrong apologies for misunderstanding
Pretty much just skip a custom domain and that should be enough for loading into vrc without having to enable untrusted urls.
#udon-networking message ๐คท๐ผโโ๏ธ
So maybe the best of both worlds is, pay for GitHub so you can make the repo private but use the github.io domain
It won't show up on your list of public repos
But if someone guessed the repo name they'd know it was yours
๐คท๐ผโโ๏ธ
If someone could guess the repo, that would be worse if using custom domain. Because they would have to use their real name to register a domain name. Cant say about "free domain names" though, those are also sketchy.
WHOIS privacy services are very commonplace - my registrar includes it for free on all domains they sell, they will only give over your information in response to police requests
Hi everyone! Weโre cooking up a special update for the VRChat Worlds SDK: Reworking how networking โownershipโ is handled in VRChat and Udon. This should make multiplayer worlds more stable by fixing long-standing issues for world creators. โฆ But this means entering the โ Danger Zone โ since it may break existing VRChat worlds. Thatโs why we wa...
If someone is leaving a map I work on, can I force everyone to spawn in a lobby again to recalibrate roles? Never did that...to let everyone on a map hop back into entrance. Maybe on leave checking the role of the leaving bean and if it was the important role that needs to be put to someone else...activating a trigger that teleports everyone on playground? x_x
i have this now, but it doesn't work. is it because i did it wrong, or because i tested in clientsim?
OnDeserialization doesn't run on the person who requested it if I remember correctly
what you could do is put what you currently have in OnDeserialization() into a new function and call it inside OnDeserialization() and after changing value in OnClick()
that works, thanks
hey, am I missing something? It should be able to sync, no?
you're showing the wrong class
The issue is in MasterReceivers.cs
It's telling you that you can't sync the variable of type PunchClass[]
there's literally nothing...
What do you mean "there's nothing"? You can only sync specific types:
https://creators.vrchat.com/worlds/udon/networking/
damn, okay
I'm hoping someone can point me to a tutorial I've not seen yet. What I have is a boat as a child of an object with a hinge joint on it and Use Motor enabled. This makes the boat move in a circle, but the boat is in a different location for every player and I'd love for the boat to be in the same location for every player. Can this be networked?
Put VRCObjectSync component onto the boat with rigidbody.
awesome. easist fix to what a thought was a complicated problem. thanks!
How is one intended to sync a jagged array in UdonSharp? I absolutely need to sync a Color[][][] (for a game mode in my game), however I don't know what to do instead of a single Color[][][]?
Jagged array can't be synced.
Instead, index a 1D array as 3D array, the array has length of length * height * depth and element index is array[(i * length * height) + (j * height) + k].
I'm currently unable to test my code, because the offline-test does not let me join in my offlineworld for some unknown reason
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UdonSharp.Video;
using Mono.Cecil;
namespace SaphiAssets
{
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class SetPlaylist : UdonSharpBehaviour
{
public USharpVideoPlayer video;
public VRCUrl[] playlist = new VRCUrl[0];
public bool loopPlaylist;
public bool streamMode;
void Start()
{
}
public void setPlaylist(){
if (!Networking.IsOwner(gameObject))
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
}
if (video.CanControlVideoPlayer())
{
video.TakeOwnership();
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, "setPlaylistEvent");
}
}
public void setPlaylistEvent()
{
video.playlist = playlist;
video.loopPlaylist = loopPlaylist;
if (video.CanControlVideoPlayer())
{
if (Networking.IsOwner(gameObject)){
video.TakeOwnership();
if(streamMode){
video.SetToAVProPlayer();
video.SetNextPlaylistVideo(0);
}else{
video.SetToUnityPlayer();
video.SetNextPlaylistVideo(0);
}
video.PlayNextVideoFromPlaylist();
}
}
}
}
}
This should set a playlist of a UdonSharp Video player, and sync it to all players.
"setPlaylist" is invoked via a button
the line "using Mono.Cecil;" caused an issue
Is OnDeserialization() called on the client that called RequestSerialization(), or no
No. Instead, the owner will get OnPreserialization which happens before sending the data
I see, whilst I'm here, what is the average latency for Manual synchronization (if it's being called once per second or something close to that)
depends on the amount of data being sent, but if it's a small amount such as less than 500 bytes, it should be about 0.2 to 0.5 seconds
I see, if it's just a boolean or a basic integer, it generally shouldn't take long then, I'd assume
yeah, manual is the fastest way to do that
I'm pretty new to networking so this is one hell of a learning curve to get a hold of, but I'm hoping I can get it to work.
Speaking of, would it be possible to make a system to detect once everybody has received the data that has had serialization requested? I have a large array (2304 elements) that might take a moment to sync and I can't let the game start until everybody has it
More robust: Use a player pool to sync whether or not everyone has received it
Simpler: have each person send a network event and count that you have received the correct number.
I'm uhhh, a little dumb, how would I use a player pool for that?
Interesting idea... I may consider using such a mechanism. Keep in mind it is binary information, simply active/inactive so you can't pass data along.
VRC Object Pool provides a lightweight method of managing an array of game objects. The pool will manage and synchronize the active state of each object it holds.
Objects are made active by the pool via the TryToSpawn node, which will return the object that was made active, or a null object if none are available. Objects may be returned to the pool by the pool's owner, and automatically disabled, via the Return node.
When objects are enabled by the pool, the OnEnable event is fired, which an UdonBehaviour on the object may listen for. Note that the OnEnable() event fires before the udon Start() event.
Late joiners will have the objects automatically made active or inactive where appropriate.
You can see if you have a pool and each player spawns one in response to your sync event you can count the number of OnEnable events that occurred. After a little time each player could return the object to the pool so it was available the next time you needed it.
That's an interesting concept, I'll definitely look into that
I would not recommend that, that's not what I meant. vrc object pools still have a limiting factor - only one person can own it at a time. If multiple people try to take ownership and spawn things at the same time, they will overwrite each other. It is not guaranteed that each and every person's change would be accounted for. This would have all the same limitations as a simple manual synced behavior with a synced int
What I meant by a player pool is a system that assigns a unique object to each player, so everyone owns their own separate unique object. If you're not fighting over objects, you can be certain that all the data is intact.
this is one example of a player pool: https://github.com/CyanLaser/CyanPlayerObjectPool
So I'm assuming I make an U# script that goes on that unique object, and that stores variables like whether it's synced the colours or not?
Oh thankfully that was cleared up... there is a lot more power in having the objects sync data of course. I'll think about it more on those lines because I definitely think some inter-player comm system would be useful. I'm tossing around the idea of compound values (arrays for instance) that could include the recipient, the data and "parameters". It shouldn't require the pool.
Yes. Basically sync something like "isThingReady" and then track in the game manager if everyone who is in the game is done or not.
Does IsUserInVR work correctly when checking remote players, or is it supposed to be a local method?
A VR player didnโt seem to notice I was desktop when some code ran on his end last night, but when I checked with two desktop players it seemed fine
It should work as expected for remote players too.
๐ knowing that is good enough for me, but Iโll double check it tonight ig ๐ค
@cold laurel I feel like I never heard of you then you appear in Argus and now youโre one of the most active code helpers I see ๐
Thanks for all the support ๐
np! 
Question:
A player who is not the owner of a networked switch (with a synced boolean) toggles it.. between these two methods:
โข Transferring ownership to the local player and then activating the switch
โข Sending a networked event to the owner to toggle it
What is the comparison between these, Is one method notably better than the other?
Or is this negligible?
uhhh depending what does the toggle do you might want to transfer ownership to the local player when he interacts with it, that way he gets instant feedback
because if he had to send network event to the owner to toggle it there would be tiny delay for him to see the toggle for example open or close the door
Right, so essentially it would be better to just transfer ownership, I imagine the owner transfer runs on all clients in order to update who the owner is, but the cost of that action is probably minimal
So I think Method 1 is better in the mass majority of cases
@shy path @rocky summit This is typically incorrect. Ownership transfers take more time as the instigator sends a request to the Object Owner and the Owner can either approve or deny the transfer request. All of the clients then have to be notified of the ownership transfer and then the instigator can send events to other clients.
If the ownership is going to be kept for a long time and the instigator is going to do a lot of event sending and receiving, then the transfer can be beneficial beyond the initial cost, but for a simple network synced toggle, sending the owner a request to do something is the fastest thing you can do for everyone. The only case I can see it necessary to transfer ownership in this case is if you need access to the instigator's player object.
What you're describing is the behavior when you have implemented OnOwnershipRequest. When a script has this event implemented, it changes the behavior of ownership transfers to make them require sign-off by the original owner. But, even in that case, the person taking it will still see themselves as the owner immediately and be able to change values with no delay. It's just that a moment later they will receive a response back from the previous owner, and either they will be confirmed as the new owner or they will be denied and the object will be returned with variables reverted.
When you do not implement OnOwnershipRequest, there is no confirmation or extra round trip, so the cost is minimal.
Regardless of which way ownership transfers happen, they both share something in common - when you take ownership, you have it immediately and can set variables directly. This is good for giving players instant feedback when they click something, as the state of the world can match the state of the synced variables directly. Synced variables are guaranteed to be eventually consistent which means that it's possible for clients to have a desync momentarily, but eventually, they will settle. As long as you are properly using the synced variables to drive the state of the world.
The problem with using network events is exactly as @shy path said - latency. When you send a network event to the owner and tell them to change it, you don't get that immediate feedback, you have to wait for them to send the state back. Unless you fake it, and set the state early, before they have gotten back to you. The problem with doing that is that you would be throwing away the guaranteed eventual consistency. There are likely certain race conditions and orders that things could happen where you could get permanently desynced.
This is similar to why I would not recommend just using network events for syncing state without variables entirely. They do not have the same guarantees that variables do, and you're entirely on your own to deal with that problem.
I would not recommend network events for simple toggles because you have to choose between latency or potential desyncs. A quick ownership transfer and setting the variable is best. That being said, if this object is more than just a simple toggle, there are other factors to consider. Having more synced variables on this object would increase the overhead of taking ownership and sending variables. Frequently transferring ownership may also present problems for certain systems that constantly need to account for that. In those cases where transfer is not wanted because it is just a single toggle embedded within a larger system, I would likely go with the network event solution and just eat the latency cost, without trying to sneak out of it.
My world's risk for that happening is next to nothing so I can tolerate something like that which I do, I guess it could come down to how tolerant someone is to these types of things. Beyond that, for my network toggles, I have the owner set a state bool that is synced, so it's eventually synced and what's on remote is preferred though this only happens on deserialization on a manually synced object. If the object is never toggled again, then yeah "permanent desync"
So many posts about hypotheticals in a world where we are able to demonstrate reality. I and many others use synced variables. The system is documented, well understood and works. If there is a better solution, i.e. simpler, faster, more reliable would you (or someone) post an example? That way we can all learn the better system. I for one do not understand what "my world's risk" means. What is present or lacking such that synced variables would be the inferior solution?
I think you misunderstood my system. What my system does is a user sends a custom network event to the owner who flips a bool to toggle something. That bool is udon synced. My world doesn't have much risk associated with a client locally changing the state before the owner sends the actual state for an instantaneous effect. This is in opposition to a user taking ownership over an object to change the bool.
Perhaps I also did a bad job at explaining what I do
Right, if you are not setting the state early then you are correct, there is no risk there. But that adds extra latency. By contrast, taking ownership and setting variables can set it instantly with no latency. And you would be right that that could have risks associated with it, but synced variables and ownership transfers have been built precisely to fix that problem and ensure eventual consistency
either way you do it, setting the state early before you've confirmed the change can result in desyncs. The difference is that network events do it blindly, while synced variables are heavily controlled and built to handle exactly that problem
and I'm okay with accepting that risk
it's.. not.. what 
Here Ill just send code
using UdonSharp;
using VRC.SDKBase;
using UnityEngine;
namespace Murder
{
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public abstract class SyncedInteract : UdonSharpBehaviour
{
[UdonSynced] private bool state = false;
[HideInInspector]
public bool _local_state = false;
private bool _waiting_for_remote = false;
public override void OnDeserialization()
{
SendCustomEventDelayedSeconds(nameof(_TriggerOnDeserialization), 0.1F);
}
public void _TriggerOnDeserialization()
{
if (_local_state == state)
{
// The variable is now updated to match remote.
if (_waiting_for_remote) _waiting_for_remote = false;
return;
}
// The local state does not equal the remote state immediately after interact. This should be checked to provide a smooth experience.
if (_waiting_for_remote) return;
_local_state = state;
_OnStateFlip();
}
public override void Interact()
{
_local_state = !_local_state;
_waiting_for_remote = true;
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner, nameof(UnaryNegateState));
_OnStateFlip();
}
public void _ResetState()
{
if (state) SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner, nameof(UnaryNegateState));
}
public void UnaryNegateState()
{
if (Networking.IsOwner(gameObject))
{
state = !state;
_TriggerOnDeserialization();
RequestSerialization();
}
}
public abstract void _OnStateFlip();
}
}
good lord that's just way more compliated than it needs to be. The network event is also just a generic flip, and does not specify which state to change it to, which means you could easily end up fighting the latency and flip it the wrong way if multiple people click it at once
I did experience that, yes though that's how I want it to be
It's meant to be chaotic

look, you're welcome to make that design decision for your own projects but it's incredibly important that you don't spread that to other people's projects where they don't want that element

I will relent that yes I can very easily just have events to set to specific states and that will resolve fighting
Alright ๐
How would you have it, then?
this is all you need, seriously it's this easy
using UdonSharp;
using VRC.SDKBase;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public abstract class SyncedInteract : UdonSharpBehaviour
{
[UdonSynced]
private bool _state;
private void Start()
{
_OnStateFlip();
}
public override void Interact()
{
if (!Networking.IsOwner(gameObject)) Networking.SetOwner(Networking.LocalPlayer, gameObject);
_state = !_state;
RequestSerialization();
_OnStateFlip();
}
public override void OnDeserialization()
{
_OnStateFlip();
}
public abstract void _OnStateFlip();
}
How does VRC handle multiple people trying to take ownership at the same time?
it automatically compares the timestamps and the one who did it last gets authority
and if people continue to try to take ownership? Does it do this every network tick?
no, it handles it when it receives messages that people try to do it
if they continue to try to take ownership, there will be temporary desync. But the moment everyone stops, it will settle on the last message
I'd probably also throw in a ```
private void Start()
{
_OnStateFlip();
}
to ensure that the state matches the variables at the beginning
I think Iโm having a hard time understanding this - How exactly does it do this if the requests aren't received at the same time? Does it enqueue the transfer and then defer it to another time to see if other users attempt to or do all of the transfer requests come in at the same time?
Every message you send comes with a timestamp. There is a server in the middle that relays messages back and forth between the players. The server uses those timestamps to track who did what and at what time. If multiple things happen within a short time span, it can discard old messages that have since been overwritten. But it will never discard the latest message, and will ensure that everyone has received that latest state
As an aside... one of the measures of code is the level of effort to do what are considered routine things. The developers of languages and APIs don't go out of their way to make adding numbers and rotating objects hard (or impossible) so if one finds themselves writing all sorts of code to handle "cases where this could be wrong" it may be time to revisit one's code.
Are there any know issues with checking component objects on synced stuff? I have a toggle that enables something on a gameobject component that is on every avatar, in 2019 it works fine but in 2022 it will CTD any vrchat client that isnt the person who clicked the toggle.
Yea after a bit more testing, its a 0 error instant CTD and I literally dont know why
and I just confirmed it happens to every client that isnt the one to click the button
same script functions flawless on 2019
what do you mean by "a gameobject component that is on every avatar"? Udon cannot access avatars
its a gameobject that is tracking players, 1 object per player, acessing that object prefab to toggle on an object ctds
Have you checked out the logs? I'd be curious to know the last thing it did before saying bye bye
Yes, the log files had 0 error.
Last lines were:
2024.03.02 19:12:58 Warning - [Network Processing] Ignoring TrySetOwner attempt on Toggle (UnityEngine.GameObject) because Zack3D already owner
Repeated
For all I know, this is a weird test thing where running 3 clients from the same user just confuses 2022 when changing the owner from "Zack3D" to "Zack3D" and it just... blows up
There is always a chance that there is some super specific bug, but I haven't had any issues with transferring ownership like that personally. At this point you could just debug log everything and let it crash to see where exactly it stops.
Hey there, I haven't wrote udon in a while but I can't figure out why this script isn't working.
It's a manually synced toggle in which a player interacts, takes ownership, changes variable and requests serialization. Then the player calls a method to do stuff and the remote players call the same method triggered by onDeserialization. The code works fine for the owner of the object but any remote players instantly crash when the player interacts with it.
Any thoughts or solutions would be much appreciated ๐
have you tried debugging the cause of the crash? have a look at this https://udonsharp.docs.vrchat.com/runtime-exception-debugging/
You will often find yourself with programs that can only be debugged in-game. In order to catch these errors and make them easier to understand, U# includes a runtime exception watcher that will look for exceptions from VRChat's output log. The watcher will then output the script and line that threw that exception to your editor's log.
I will give it a go thank you!
It would be a lot easier to read and comment if you were to post the code with code markdown. I'm not going to type all that code into an editor. You have several odd choices. The most notable is your insistence on handling OnPlayerJoined with a custom event. It isn't necessary and there are no cases where VRChat "bugs" and does not sync on join. If it did you can report it but in any case requesting serialization isn't a fix.
Note also that your Toggle method only varies by a single value. SetActive is true or false depending upon StartingState. Nothing else is different so the if is not needed and simply setting the active and toggle to the value of StartingState is equivalent.
It used to bug in the past, players would fail to sync on join
This code is old
I believe it's fixed now, just a quick copy paste edit
One sec I'll include the code
Your issue could have something to do with your setting of the UI toggle. One must be aware that setting the value like that will fire the toggle event. If you handle that somewhere it can end up in a loop.
Oo I see
Ah thank you I will check. I forgot there was a way to toggle without triggering the event I think. That is probably it! ๐
โค๏ธ
I appreciate you so much
I've been scratching my head
Will update if it works
Yea I probably will at some point, im just not too worried right now because it was just a 'debug enable' thing. Not even needed for the final product.
hey, does anyone know if player tags are synced? like, going off the example in the documentation, if a new player were to join, would that player be able to see the fact that someone else in the instance had previously been given the "chef" role? or are these simply local tags
also, if they are synced, does anyone know at what time they are synced ? OnPlayerJoined ? the first OnDeserialization call ? etc.
Documentation page mentioned: https://creators.vrchat.com/worlds/udon/players/getting-players/#setplayertag--getplayertag
Player tags are not synced, but the upcoming playerdata feature is
yea, really looking forward to that ๐ but okay noted, tysm !!
oh btw, does anyone know if the whole "variables won't sync immediately after setting an object's owner" bug has been fixed yet? (since I know some sections of the docs can be a bit outdated)
like, can I just assume that
Networking.SetOwner(localPlayer, gameObject); syncedVariable = value; RequestSerialization();
will work reliably, or do I have to implement some wonky Update loop to wait/check for successful Ownership transfer requests for sync to be reliable?
the bug in question: https://udonsharp.docs.vrchat.com/networking-tips-&-tricks/#known-issues
That's suuuuper old
It is now entirely safe to take ownership, set variables, and request serialization all at once
oh okay awesome !! that's a relief then, tysm for all your help ! (:
Hello, very beginner question but we can't mix sync modes on a single behavior right ?
I have a behavior with a float property that should be updated continuously and a Vector3 property that should only be set at specific times, so the ideal solution would be to have the float on "continuous" and the Vector3 on "manual", is that an option ?
no, use two behaviours or accept the overhead. are you sure you need float as continuous? not the most popular data type for that.
oh sorry had a brain fart
for some reason float registered as bool in my head
Haha np. About the types, this is for syncing a rope's visual, so I need its length (the float) to be updated constantly as the player moves, while its target position (world space) can be set only once the rope is attached
Right now the behavior is synced continuously, since this is only visual it's not a problem if some data is lost
but I was wondering if there was a "cleaner" way to do it (answer seems to be no)
You have the rope attached at one end to the Vector3 and at the other to a player? Sync the Vector3 and the player ID the rope is attached to (via manual sync), and draw the rope in PostLateUpdate(), when the player position is up to date.
I should probably try that at some point !
Will make a bigger post later with 1) what I'm trying to do, 2) what I tried and 3) how it doesn't work, but just to summarize I have a shader that draws the rope using a target world position and a length (using its own transform as the origin), so I thought I could use a VrcObjectSync to sync the rope's transform and a U# script to sync the target and length (and update the materials).
I have an issue i've been trying to figure out. I have an gameObjectA that gets spawned by a VRCObjectPool and within gameObjectA is another gameObjectB with and udon script that is set to UdonBehaviourSyncMode manual. Within the same update cycle I am spawning gameObjectA, updating a UdonSync variable in gameObjectB and then sending a RequestSerialization. The result is the gameObjectA being spawned, but the variables in gameObjectB are not synced.
I believe the reason for this is because both changes occur in the same network update so the gameObjectA (and gameObjectB) get spawned but since the udon script for gameObjectB hasn't started up yet it isn't able to receive the variable changes.
I've tried to make a workaround where the owner of the object will send a RequestSerialization every second (as a less frequent version of continuous sync behavior) in an attempt to update gameObjectB variables after the game object has spawned. However when testing this it seemed like take multiple resync attempts to finally sync up between players. My testing had an average of around 103 gameObjectBs that need to be synced and took as long as 3 minutes before everything looked like it was synced up. The variables that needed to be synced were two bools that effected an animator on gameObjectB.
Has anyone else had this issue when trying to sync variables with a freshly enabled gameobject? or know a good workaround for it?
Are your synced objects disabled by default? I think there is some kind of initialization that happens when the object is enabled for the first time. So you should try to leave them on by default so it does the initialization on startup. This way, when the pool enables the objects, it might actually work without delay, and maybe even sync without having to resend data.
Although the VRCObjectPool always had some quirkiness to it in, my experience at least. You might want to write your own if all else fails.
The synced objects are part of an object pool, so they get disabled by the pool at the start. Whether that happens before each object has a chance to startup or not I'm not sure. I believe I enabled all the objects in the pool, but I'll have to double check. I guess another thing I could do is spawn every object in the pool once before despawning them so that they have a chance to startup.
Thank u for your input
after checking my scene the synced objects are enabled, but they are disabled by the VRCObjectPool when playmode starts.
What about the sub objects that are manually synced? Are those enabled? Just making sure here. But it might very well be the pool doing stuff under the hood, I remember having similar issues. Sorry, I don't remember the specifics. I just switched to my own very simple pool and never had issues like this since. I can post it if you want, although I'd need to strip it down slightly.
The sub objects (gameObjectB) are always enabled locally, however they do get disabled globally when their parent (gameObjectA) is disabled. I think i'll do afew more tests with trying to get all the udon behaviors to start up before looking into modifying the pool.
It definitely sounds like you are facing the same issues I had. So I'd personally suggest to save yourself the trouble and try another pool. If you want, here is mine that has a similar API as the VRCObjectPool, so you can quickly swap it in and test it out. I'd bet it will work much better.
So while idk the exact details of your networking setup or the type of world you're trying to make, apologies if it's a bit of an obvious solution, but I'd also like to suggest the classic "if possible, try moving all your sync logic to one, always-enabled GameObject that controls all those other GameObjects" just in case you haven't considered that.
i had a similar issue in my world recently, and that was my solution. maybe have arrays for the variables of each object, have the objects and the controller interact with each other, etc.
I hadn't thought of an always-enabled GameObject, that definitely sounds like it might work. One of my concerns with that solution though is that my project is a kind of tool that other users can use. The number of GameObjects the always-enabled GameObject would need to keep track of would be alot (my testing example had around 103 of them) which could potentially lead to a huge DataDictionary that could exceed the limits. The idea seems like it would mesh well with another idea I had for a future feature so I'll trying testing out the limits for this. Thank you for the suggestion.
Ah okay, well yea of course! Glad to help (:
Btw, has anyone used [FieldChangeCallback] with synced arrays? I know I have to change the entire array for the callback to fire, so I do, and that works locally, but it doesn't even try to run OnDeserialization() on remote clients despite the owner successfully firing RequestSerialization() and OnPreSerialization() after that first local callback successfully fires.
When an array is synced, does Udon just update the relevant elements in the array that have changed? Or does it assign an entirely new array and fire callbacks properly? And regardless, would anyone happen to know why OnDeserialization() wouldn't even bother firing like that?
Notes: The object with the sync logic is always enabled. Relevant code snippet attached in case it's any help (The code in
OnDeserialization()is just meant to print log messages that communicate whether or not it ran).
Update: It only fires OnDeserialization() if the array is initialized to something like syncedIndexes = {"!!!", "!!!", "!!!"}. For whatever reason, new string[3] doesn't seem to work
But now it's still not triggering the FieldChangeCallback, which leads me to believe that arrays just get modified when they're synced, and not replaced...
Oh well, I can work with this now at least, but I'd still like clarification on that if anyone knows
This is because the elements in the string array are null.
And VRChat refuses to sync arrays that are null, or have null elements
well the weird thing about that is that I've had the array initialized to new string[3] for about half a year now with no issues ๐ญ i think i was initializing elements to "" somewhere though, so ig that makes sense.
thank you for clarifying !
do you know if it replaces the array OnDeserialization tho ? or if it just updates the elements of the array that have changed ?
~~A new array is allocated on every deserialization. ~~
It also serializes the entire network-data of each GameObject that has requested serialization upon serialization.
It does not allocate a new array if the existing array is already the right length. It does replace everything in the array with the synced data though.
Ohh then I was misinformed
that used to be how it was to be fair
it changed to reduce allocations like 2 years ago
yeah this was my theory, that makes perfect sense. so then I was correct in assuming that that wouldn't trigger a FieldChangeCallback
right, it doesn't trigger fieldchangecallback when you change a value. It never did. It only triggers the callback when the array itself is switched to a different array. On remote users, that only happens if the length of the array changes. If the owner changes it to a different array of the same length, the owner gets the callback but remotes don't.
It used to just trigger the callback on all deserializations because it was always making a new array, so people wrote code for that assuming that it was indicating some value inside the array changed, which was never the case.
i am not in play mode ._.
How do I detect when two player colliders, well, collide?
place generic triggers on players
I need a little help with my system, I don't know the correct way to do it
I have the following, my system uses an Synced int to control the activation of objects.
When someone clicks on a button, I want the owner to process the ID and then send a custom event so that the object with a certain ID is activated.
my first approach was to the Owner change the ID, then RequestSerialization and then send a SendCustomNetworkEvent.
The problem I encountered was that sometimes the send custom network event was received first than the ID change, thus breaking the system.
my second approach was, do a FieldChangeCallback for my ID int.
then when the Owner changes the ID and the ID change was detected, it executes.
This solves the problem, but now when a LateJoiner enter the world after a object is activated the last object is activated erroneously.
the expected behavior is that the person only sees the object when someone presses the button
.
If you want it to work with late joiner, then you shouldn't be using SendCustomNetworkEvent, and purely relies on OnDeserialization event.
OnDeserialization dosen't Trigger when the Late joiner enter the world?
It does trigger.
I just checked, OnDeserialization triggers on the LateJoiner when enters =/
Of course
How can I do it so that it doesn't activate the object erroneously?
that only activates when someone presses the button
i think i figure out of a way*
What "activate the object erroneously" really does?
The idea is this, every time someone presses a button the ID is calculated and then the object with that ID must be activated for all players on the world.
but if someone joins the world after that, no object should be activated
Try serializing object activation timestamp, and use that to compare with a player joining timestamp. If a player join timestamp is greater than object activation time stamp that measn the player join after and don't activate the object.
SendCustomNetworkEvent can't carry data.
I know ;-;
I'm trying to access the DeserializationResult
but I don't know how to do it ;v
public override void OnDeserialization(DeserializationResult dr)
Why you'd want to?
The documentation says that DeserializationResult have sendTime
that is The time in seconds at which this message was sent
basically I can know how long ago it was sent.
I can use this to know if I need to activate the object or not
you can just access with dr.sendTime
I am getting this error,
error CS0246: The type or namespace name 'DeserializationResult' could not be found (are you missing a using directive or an assembly reference?)
I'm looking in the documentation for what I should include to be able to use the DeserializationResult
VRC.Udon.Common
Thank you
I just thought of a really dumb way to solve this.
always ignore the first OnDeserialization;
If I'm intending to network a lot of values, does using smaller data types (i.e. short instead of int) allow me to continuous sync more values, since a short is smaller in memory than a regular 32-bit integer?
Yes, it does. If you can get away with smaller types, use them
It will use less bandwidth
I see, and is the 200 byte continuous sync limit per-script or totalled across all scripts?
Per object. If you have multiple scripts on an object it's shared
Does not extend to children
Is there any upper limit for how much bandwidth a world can use in total for syncing?
Or can you just have as many syncing behaviours as you want (up to the point that either Photon or the player's network gets overloaded)
Depends on several factors, but it's between 2kbps to 10 kbps
Manual sync can handle much, much more per serialization. You're not limited to 200 bytes, it's more like 65000
Also depends on how many serializations you make per second.
almost 0 data with many serializations will still clog the network
Well yeah, there's the overhead of the sync itself. Just because you don't have any user data doesn't mean you're sending nothing
Mhn

Also, why is SimulationTime closer to Time.realTimeSinceStartup for VR players than desktop players?

yes
How much?
vr players are generally less than 0.25 behind
while desktop is generally more than 0.25
Oh right you mean further ahead yes. I thought you were implying VR was behind
Yes that makes sense because desktop players sync at 5hz, vr players sync at 10hz
ah
that's unfortunate
I guess I could crank up the serialization rate to compensate
but uh, I'd rather not spend 1/4 of my serialization budget per object

So I ended up making my own SimulationTime that lags behind just enough for 4hz
Which desyncs the movement compared to the locomotion animations
but eh

Well, it depends what your purpose is if using simulation time. If it's to match the player then that's still what you want. If it's behind that's because the player is behind too
I'm manually (de)syncing player position and rotation
because the world isn't in world space
Then yeah I'd use the players simulation time because you want the player's ik to match their position. It's higher latency yes, but that matches desktop players
but then I have to spend 10 serializations per second just for player sync if you're in VR
Along with any pickup you might have
so I can have a whole total of 3 pickups before hitting 40 per sec
Yes? That's what you're committing to if you want to sync your own player position
Fair enough
I just wish we had another sync mode
like, and unreliable manual sync made for high frequency and low bandwidth
This is why objectsync switches to a higher rate while held and doesn't allow udon to just arbitrarily set it to use that high rate
Which is something that people have wanted for a while but can't be done because bandwidth limits
We're all beholden to the same masters - photon
Wait really?
Are you able to elaborate on what those limits are set at?
It just seems ridiculously low at the moment
The systems which limit bandwidth are pretty arbitrary and do not have human-readable limits. We're still discussing ways that we can translate or measure that to be more meaningful to creators.
Hi there, I'm also working on a system for my world, here are my intentions:
After two players are near each other and collide over a certain amount of time, GameObjects will instantiate around them in an explosion, do a little bounce animation, then return to the play and the world will keep a count of how many GameObject each player's collected and a world total.
Here is my Code so far :
Are there any suggestions or tips, I believe I also need to develop the Follow player/Return count system.
Please Ping me <3
My code does not seem to be working, I think it has to do with the colliders. Does anyone have alternative
You are trying to detect players entering/exiting colliders? Look at OnPlayerTriggerEnter/Exit() rather than OnTriggerEnter/Exit.
I'm trying to detect players colliding with other players.
I get his Error with this edit
Assets/Cuddle system/CuddleSystem.cs(27,4): Method with same name as built-in event 'CuddleSystem.OnPlayerTriggerEnter(UnityEngine.Collider)' cannot be declared with parameter types that do not match the event.
My 'Game objects' do not seem to be spawning/Instantiating and I'm not sure why.
Full Code:
I believe that one takes a VRCPlayerApi parameter. You get the player collided with, not the collider.
There are triggers:
https://creators.vrchat.com/worlds/udon/players/player-collisions/#physics
Then theres the VRCPlayerAPI:
https://creators.vrchat.com/worlds/udon/players/getting-players/#setplayertag--getplayertag
these are the documents i used, but i cant seem to find a solution
Udon has three ways to detect when a Player and an Object Collide - Triggers, Physics, and Particles.
These nodes are useful for getting an individual Player, a group of them, or all of them.
I write new code, Unity crashed when i attach it to the udon Behavior component :( when does it eeend
OnTriggerEnter must be attached to the same gameobject which have a trigger component, and you can't access to the players gameobject via Udon. Also note that remote player trigger looks like this image. This is still something that they have to fix afaik so meanwhile you might have to calculate distances manually in your update method
So what youโre saying is that since I cannot access the Playerโs Collider I must make a new game object with a collider. Attach it to each player in the instance and detect collision with those?
question, if i make someone the owner of an object, does it affect the children of the object too?
This is a list of Udon Nodes that are considered "Events".
it does not
alright thanks!!!
yes this is the workaround I use for my deadly pan that can headshot and turn people into ragdoll, but instead of calculating every player, I only use 1 trigger to the closest player based on VRCPlayerAPI tracking data. But as Kitkat said, if you don't need a specific position, you can just use onplayertriggerenter
how do i make this synced for late joiners? it works for people who are in the instance, but if someone joins late, the Unlocks[] won't be enabled, and the button itself won't be disabled
these are the relevant variables
Because you tried to mixed one-time event with synced variable. If you wanted to use synced variable, you shouldn't use network event to update the variable. Instead, RequestSerialization and trigger the change react on remote client with OnDeserialization #udon-networking message
And NetworkEventTarget.All to buy, that mean every one in the instance will try to take ownership to update the value at the same time.
alright, i can mess around a bit more then. The main reason i used a network event was because i was translating a graph to U#, and i didn't account for that part
Even with graph, using network event isn't the right way to begin with.
i did make it about a year ago, so i knew even less about networking than i do now
Bought property setter shouldn't deduce the money, because that function is used for reacting with change in value, and it also triggers when late joiner got synced. That mean every time someone joined, the money got deduced.
so, should i put the
money -= cost;
moneyManager.SetProgramVariable("money", money);
into the Buy() method instead?
Maybe. Also keep in mind there should be only one player to own and manage the synced variable.
after watching the full video you linked, i'm wondering; do i still use a getter/setter, or should i just use OnDeserialization instead?
ok this is what i have now, it seems more correct but did i do everything right?
(i changed line 76 to SendCustomEvent(nameof(EnableUnlocks)); because i realized it makes more sense)
so, while it does mostly work, it seems that a lot of things will already be bought when a second player joins (for that second player) even though they shouldnโt be. and if i have a button that unlocks other buttons, all of those will already be bought aswell, even though they shouldnโt be either
Yeah so that's a good call, you don't have to call OnDeserialization yourself, it's automatically called when the network owner changes the variable and requests serialization. So when the other players receive the new value from the server it will execute whatever is in OnDeserialization. This is where you'd have logic to check the states and values of your synced variables and execute your other methods to have their end match the client that changed it
What you could do if I'm understanding this right is, within your custom EnableUnlocks, have that event check the udon synced variables and enable/disable things accordingly. That way when it detects a change and runs OnDeserialization, it'll send the local event but within that event it references the synced variables to enable/disable stuff accordingly in theory . Not sure if your using many udon synced variables but that's what I'd play around with and your setters can set the states of objects based on those vars
well, the money varible iโm getting from another script has [Udon Synced, FieldChangeCallback] and the โboughtโ variable only has [UdonSynced]
otherwise the other variables arenโt synced
iโm not really sure what you mean, cause i still donโt fully understand these getters, setters, and serialization events, but i added a check at the beginning of EnableUnlocks, where if โboughtโ is false it does nothing. is that about right?
There are a few steps you should consider in an effort to better understand code. First you should post your code as "code" not as screen captures. It is barely readable and obviously not editable as an image.
Second you might take a day to better understand C# code conventions. It isn't that not following them creates a direct problem but rather inventing a new convention generally doesn't help. So I don't know if you copied the code but take the Debug.Log statement for example. What is the benefit of the markup? I know it will display in color but if coding is the issue then markup is just getting in the way of being able to drop lots of Log statements in your example.
It looks like the Buy method could be private. The { return; } statement at the top doesn't require the braces.
And more ๐
Keep things simple to reduce unintended issues.
do you have any good sources on that youโd recommend ?
iโd be happy to learn
For C# conventions pretty much just reading from Microsoft .NET documentation.
alright
The docs are definitely a place to hang around but it won't "teach" you stuff. I think it is more about practice and that doesn't have to be a game or a world or anything. It is just setting up a situation, a problem that you need to solve Resize something, change it's color, position, rotation, etc. Make it bounce, have it make a noise. And that has to all be networked. Code eventually starts to make sense because of the repetition.
this is the code as a text block btw, so someone could actually help
[UdonSynced, FieldChangeCallback(nameof(Bought))] private bool bought = false;
private float money;
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!player.isLocal) return;
money = (float)moneyManager.GetProgramVariable("money");
if (money >= cost)
{
Bought = true;
}
else
{
Bought = false;
Debug.Log($"<color=lightblue><b>{this.name}:</b></color> <color=orange><i>Player did not have enough money!</i></color>");
}
Buy();
}
private void Buy()
{
if (bought)
{
money -= cost;
moneyManager.SetProgramVariable("money", money);
}
Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
RequestSerialization();
SendCustomEvent(nameof(CheckBought));
}
public override void OnDeserialization()
{
SendCustomEvent(nameof(CheckBought));
}
public void CheckBought()
{
foreach (var item in unlocks)
{
item.SetActive(bought);
}
if (previousObject != null) { previousObject.SetActive(!bought); }
if (soundEffect != null && bought) { soundEffect.PlayOneShot(soundEffect.clip); }
this.gameObject.SetActive(!bought);
Debug.Log($"<color=lightblue><b>{this.name}</b> bought was {bought}!</color>");
}
private bool Bought
{
set
{
bought = value;
CheckBought();
}
get { return bought; }
}
also, i just add the markup cause i find it kinda fun. it's not really necessary
(i also do it after i finish everything else, so i'm not actively adding markups while making the logs)
so, i changed the code a bit to what i thought was better, and edited the code block above, but now it just isn't synced at all somehow..
You'd have to check where the code doesn't execute. Or post detail where the code did execute.
the code block is much better (and readable now). You might want to describe your goal also. Some things are just complex for no apparent reason.
public override void OnDeserialization()
{
SendCustomEvent(nameof(CheckBought));
}
what is the point of a custom event here? It does far more than check whether something has been bought and it is called as part of the Bought setter? Quite atypical.
I've never understood why people call GetProgramVariable either. I've never, ever done that once.
-
my goal is i want to enable the
Unlocks[]and disablePreviousObjectif themoneyis greater than or equal to thecost, and have it synced for all players and late joiners. -
do i not need GetProgramVariable? the reason i used it, is because i used graph for much longer than U#
-
i have OnDeserialization call the "check bought" event because i was trying to copy from the picture below.
I don't write graph but I think there are peculiarites due to the graphical nature of the code. Assuming it has been converted to UdonSharp I don't think GetProgramVariable is necessary. You can reference the value of object properties directly in UdonSharp. And I don't believe that raising that event in graph was needed (maybe I don't know) but rather it was a choice that was made. No need in UdonSharp however because you can just call the method directly.
I don't get what you are doing but notice how OnPlayerTrigger is written for example. The if test can be written a bit more succinctly but importantly you get a log message in either case (bought or not) for debugging purposes.
Bought = (money >= cost);
Debug.Log($"{name} Bought={Bought}");
You then call Buy (whether bought or not) and sync (whether changed or not). It's confusing.
so, i think iโm gonna sort of restart with the script, and go from there. i also feel like a lot of it is unnecessary and overcomplicated, so iโll re-write it how i would if i wasnโt thinking about syncing yet
okay, back to basics. here's my script, how should i make it sync the right way?
private bool bought = false;
private float _money;
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!player.isLocal) return;
_money = (float)moneyManager.GetProgramVariable("money"); // I'm using GetProgramVariable right now, because "money" is private.
if (_money >= cost)
{
bought = true;
_money -= cost;
moneyManager.SetProgramVariable("money", _money);
EnableUnlocks();
}
else
{
bought = false;
}
Debug.Log($"<b>{this.name}:</b> <color=cyan>Bought = {bought}.</color>");
}
public void EnableUnlocks()
{
foreach (var item in unlocks) // Enable the Unlocks[]
{
item.SetActive(bought);
}
if (previousObject != null) { previousObject.SetActive(!bought); } // Disable the previousObject if it isn't null
if (soundEffect != null && bought) { soundEffect.PlayOneShot(soundEffect.clip); } // Play the soundEffect is it isn't null, and bought = true
this.gameObject.SetActive(!bought); // Disable self so this button can't be activated multiple times
Debug.Log($"<b>{this.name}</b> <color=lime>EnableUnlocks was called</color>");
}
What you're going to sync? There is nothing synced in this code.
Also you don't really need comment if the code is self explanatory enough.
yeah i know nothing is synced yet ^
what exactly i want to be synced, is the enable unlocks method
so when a player enters the trigger, i want the money, and any objects that get enabled or disabled to be synced for everyone and late joiners
You will have to decide what to be synced first. Eg, what matters for late joiner to see.
well the biggest thing that matters is that all the objects that should be enabled or disabled
the money is already synced for late joiners, cause thatโs handled in a different script
so i need the active/inactive state of the unlocks, previous object, and โthisโ to be synced
Why would previous object and "this" matter?
good point, maybe all that matters is the โboughtโ variable, since all the objects active state are set based on that
Therefore bought should be synced.
so should i simply add [UdonSynced] and use request/ondeserialization, or should i use the equivalent of OnVarChanged? thatโs what i didnโt know what to do in the beginning
OnDeserialization is much simpler to understand since it only triggered when variables got synced.
ok so, with my understanding, i thought this would be correct, but the things don't enable for everyone. ( @humble girder )
[UdonSynced] private bool bought = false;
private float _money;
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!player.isLocal) return;
_money = (float)moneyManager.GetProgramVariable("money"); // I'm using GetProgramVariable right now, because "money" is private.
if (_money >= cost)
{
bought = true;
_money -= cost;
moneyManager.SetProgramVariable("money", _money);
Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
RequestSerialization();
EnableUnlocks();
}
else
{
bought = false;
}
Debug.Log($"<b>{this.name}:</b> <color=cyan>Bought = {bought}.</color>");
}
public override void OnDeserialization()
{
EnableUnlocks();
}
public void EnableUnlocks()
{
foreach (var item in unlocks)
{
item.SetActive(bought);
}
if (previousObject != null) { previousObject.SetActive(!bought); }
if (soundEffect != null && bought) { soundEffect.PlayOneShot(soundEffect.clip); }
this.gameObject.SetActive(!bought); // Disable self so this button can't be activated multiple times
Debug.Log($"<b>{this.name}</b> <color=lime>EnableUnlocks was called</color>");
}
maybe try disabling the renderer component or a child gameobject with the renderer instead of the game object the synced script is on?
All the code seems to be getting in the way of your understanding UdonSynced variables. I would put this particular attempt aside for a moment and simply sync a single Bolean value. Have the action of the sync display/hide something or perhaps display the value as a string. You want to be sure that the basic mechanism is understood and working. That gets obscured when you introduce arrays of objects and moneyManagers and all the other stuff. If one of those parts is mucked up the thing doesn't work and you aren't 100% which part is faulty.
You can't be setting bought = false like you have done. It is a synced variable whether you choose to sync it or not. If you change the value you sync it, not sometimes, always.
It looks to me that you have _money defined as a property and are using it as a simple variable. If it only used in the OnPlayerTrigger method it doesn't need to be and shouldn't be a property.
issue is, i need the entire object to disable, because i don't want you to be able to hit the trigger again. i can't just have it go invisible
just disable the collider
well then i'd need to disable the renderer and collider, which is why i simply tried disabling the gameobject, instead of each thing separate
One should avoid two things that are commonly done. The first is directly thinking in terms of properties like SetActive and game objects, renderers, colliders and such. Think in terms of concepts and you would set the game object to "un-buyable". How that is done is encapsulated in a method. If that takes 1, 2 or 3 properties on 1 or more components it is all in that method. And it is where you can make it buyable again by reversing those settings.
The second one is don't write code in the OnPlayerTriggerEnter method. It is an event that signals something needs to occur but it shouldn't be the container of that code. It means if you wanted some other event to take care of it you would have to move all the code instead of moving one method call. It also means you could theoretically test it without having to actually set off the trigger.
alright. i'm pretty sure i know why this isn't working. i changed the code to what i have below, and considering everything works fine for the player who steps on the button, it seems that either OnDeserialization simply isn't running for the other players, or bought isn't getting set for all players
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class UnlockButton : UdonSharpBehaviour
{
[UdonSynced] private bool bought = false;
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!player.isLocal) return;
CheckBought();
}
private void CheckBought()
{
if (!Networking.GetOwner(gameObject).isLocal) {
Networking.SetOwner(Networking.LocalPlayer, gameObject);
}
float _money = (float)moneyManager.GetProgramVariable("money"); // I'm using GetProgramVariable right now, because "money" is private.
if (_money >= cost)
{
bought = true;
_money -= cost;
moneyManager.SetProgramVariable("money", _money);
if (!Networking.IsClogged) { RequestSerialization(); }
OnDeserialization();
}
Debug.Log($"<b>{this.name}:</b> <color=cyan>Bought = {bought}.</color>");
}
public override void OnDeserialization()
{
foreach (var item in unlocks)
{
item.SetActive(bought);
}
if (previousObject != null) { previousObject.SetActive(!bought); }
if (soundEffect != null && bought) { soundEffect.PlayOneShot(soundEffect.clip); }
this.gameObject.SetActive(!bought); // Disable self so this button can't be activated multiple times
Debug.Log($"<b>{this.name}:</b> <color=orange>OnDeserialization was called</color>");
}
}
is there a way to have a console in-game that shows logs? it would be helpful to test networking
(i have the !Networking.IsClogged and !GetOwner.isLocal, SetOwner things, because i copied those from a networked script i have that i know works
Good work on CheckBought... see now nice it keeps OnPlayertriggerEnter? Now you have to do the same with OnDeserialization. I'll suggest a method named Apply() and always put the stuff you need to be applied on the OnDeserialization there. That way you always know to call Apply and the player that sets the values will call Apply() rather than OnDeserialization().
There is no benefit checking for the owner before setting the owner whether the player is the owner already or not doesn't matter overall. But you shouldn't do it where you have since you haven't decided to change bought yet. You only need to assign the owner if you update bought.
I have no idea what IsClogged i checking for but I would remove it. What value can there be in skipping the RequestSerialization "sometimes"? The world (and software in general) must work or fail not skip over stuff.
Is it likely that soundEffect will be null? Again, set it and play the sound or test it once when starting.
As for debugging, some folks have a panel viewable in-game. I always thought that would be worthwhile but I've not implemented it. If you test in PC VR you get all the log statements in a file which is handy.
As a general rule I add a lot of logging to methods. It helps me track what is happening and the order of the method calls. But I recommend when you can to call them at the start of the method. A failure in the method will mean you don't get a log message if you do it as you exit.
I highly recommend keeping the gameobject with the synced script active at all times to ensure that code will run
this unlock button is part of a full system iโm working on, which re-creates tycoons from roblox, but for vrchat. the reason i do a โ!= nullโ check for the audiosource, is in case the user of the prefab doesnโt want a sound to play, they can just not set a reference to an audiosource
i do also have a little bit of documentation in a ReadMe which explains what each script does, so the user will be informed that removing the audiosource reference is fine, because of the null check
i can experiment with only disabling the collider and mesh object. because i do have the visual mesh as a separate object parented to an empty with the collider and script
i also can remove the โIsClogged,โ iโm not sure what it does either, but i only had it temporarily since i copy+pasted.
as for the checking owner before setting, in the script i got that from, it had a comment which said this: โRemoves warnings when setting owner if you were already the ownerโ
also, from what i gather, i only have to do SetOwner after i change a synced variable, and not before?
The other way around. Set the owner to the player that is going to change the variable. Then set the variable and then request serialization.
got it
well, i have this now. still doesn't work somehow, so i'm still thinking it's bought not syncing right, or OnDeserialization not running. i tried looking for in-game loggers, but the one i found was broken, and i've tried Varneon's, but there's no documentation on how to log with his console.
i posted the full script this time, in case there's else something i'm doing wrong that wasn't seen in the slightly cut versions
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class UnlockButton : UdonSharpBehaviour
{
[SerializeField] private MoneyManager moneyManager;
[SerializeField] private AudioSource soundEffect;
[SerializeField] private GameObject meshObject;
[SerializeField] private GameObject displayObject;
[SerializeField] private UnityEngine.UI.Text titleText;
[SerializeField] private UnityEngine.UI.Text costText;
[SerializeField] private string unlockName;
[SerializeField] private float cost;
[SerializeField] private GameObject[] unlocks;
[SerializeField] private GameObject previousObject;
[UdonSynced] private bool bought = false;
private BoxCollider thisTrigger;
private void Start()
{
thisTrigger = this.GetComponent<BoxCollider>();
if (costText != null)
{
costText.text = cost.ToString("$#,####");
}
if (titleText != null)
{
titleText.text = string.Format("{0}:", unlockName);
}
}
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!player.isLocal) return;
CheckBought();
}
public void CheckBought()
{
float _money = (float)moneyManager.GetProgramVariable("money"); // I'm using GetProgramVariable right now, because "money" is private.
if (_money >= cost)
{
Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
bought = true;
_money -= cost;
moneyManager.SetProgramVariable("money", _money);
RequestSerialization();
Apply();
}
Debug.Log($"<color=lightblue><b>{this.name}:</b></color> <color=cyan>Bought = {bought}.</color>");
}
public override void OnDeserialization()
{
Debug.Log($"<b>{this.name}:</b> <color=orange>OnDeserialization was called</color>");
Apply();
}
public void Apply()
{
Debug.Log($"<color=lightblue><b>{this.name}:</b></color> <color=orange>Apply Started</color>");
foreach (var item in unlocks)
{
item.SetActive(bought);
}
if (previousObject != null) { previousObject.SetActive(!bought); }
if (soundEffect != null && bought) { soundEffect.PlayOneShot(soundEffect.clip); }
thisTrigger.enabled = !bought; // Disable these, so that you can't buy the same thing multiple times
meshObject.SetActive(!bought);
displayObject.SetActive(!bought);
Debug.Log($"<color=lightblue><b>{this.name}:</b></color> <color=lime>Apply Successful!</color>");
}
}
could you elaborate how it's not syncing right? For example, people already in the instance not seeing the changes and or late joiners not seeing the changes
iโve been mostly just testing with2 clients in the same instance, and the way it doesnโt sync, is that nothing in the Apply method happens for anyone except the player who stepped on the button
Do you see the Log in CheckBought is working?
in unity yes, i get every log except the OnDeserialization one
OnDeserialization doesn't run in unity because client sim doesn't emulate network behavior.
i figured
Did OnDeserialization trigger in remote player when testing with 2 client?
Currently documentation for VUdon Logger exists in the repository's wiki, but it hasn't been included in the package yet https://github.com/Varneon/VUdon-Logger/wiki/How-To-Use-UdonConsole
Runtime logger for UdonSharp. Contribute to Varneon/VUdon-Logger development by creating an account on GitHub.
oh alright, thanks. guess i didnโt look at the github wiki
i donโt really have a way of knowing without in-game logs, but nothing happened for the player who didnโt step on the button
Debugging is how you learn about what's going on under the hood in the VRChat client and your world. It's a key skill to develop for programming in general, and for building your worlds.
So um, given the pins are 2021 whats the latest best place to learn to network sync an object so that late joiners see its state. E.g. enable/disable an object. Looking to learn what i can just not sure on what is the best method or place to find that info. Cheers.
you may want to read through this first
https://creators.vrchat.com/worlds/udon/networking/
Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
@grand orchid #udon-networking message
@frozen igloo pin it already, hard to search for it every time. ๐ซ you can always update later
AHhhh very helpful thank you...
I forgot exactly how to go about dealing with a local input field and then send the input out to everybody.
these are the only logs i see related to the button, from one of the log files when i was testing. nothing about OnDeserializaton here, or anywhere in the entire log
oh wait, i have found a different log
so it's trying to work, but just doesn't do anything?
OnDeserialization doesn't run on a player who is the owner of the object, so keep that in mind.
yeah i just wasn't sure which log that was at first
so what about this then? the remote player triggers OnDeserialization, but nothing actually happens for them?
There is no log for Apply after?
good point, no there isn't. i'm gonna try making a new log real quick though, cause i'm not sure if those are from before or after i made the Apply function
hold tf on, i just tested without changing anything, and it worked perfecly. i'm gonna try late joining now
bruh
it just somehow works after nothing changed since wednesday
only one issue though, if the non-instance owner steps on a button, money does not go down. i probably just need to SetOwner for the money manager though, because i'm not
(oh, and the sound plays once for every button purchased for a late-joiner, so it's really loud)
For sound, you could use network event to play sound because this doesn't matter for late joiner.
true
Exactly... you need to determine for yourself what is not sync'd, what is sync'd for current players and what is also sync'd for late joiners. You write the code to handle them individually.
yeah i just hadn't thought thoroughly about where to put the sound, because i was testing so long
Playing a sound seems like it is part of your "bought" processing but it is just something you choose to do at that time. Make it work independently and then you decide where to make it happen.
i was actually just adding it to CheckBought
Right but it isn't part of "buying".
well, CheckBought is where this happens, so i figured this is where it should be
public void CheckBought()
{
float _money = (float)moneyManager.GetProgramVariable("money"); // I'm using GetProgramVariable right now, because "money" is private.
if (_money >= cost)
{
Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
bought = true;
_money -= cost;
moneyManager.SetProgramVariable("money", _money);
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(PlaySound));
RequestSerialization();
Apply();
}
Debug.Log($"<color=lightblue><b>{this.name}:</b></color> <color=cyan>Bought = {bought}.</color>");
}
also, i don't really remember, does NetworkEventTarget.All mean the owner sends an event to everyone, or the other way?
NetworkEventTarget.All will send the event to everyone including yourself. It doesn't care about ownership.
oh okay. so what does the Owner target do again?
It sends the network event to the owner of the object.
okay, thanks. also thanks to the others who helped with this script, i definitely learned a good bit
this was correct, setting ownership for the moneymanager gameobject made the money go down for anyone who uses the button
also, i see now where to put the network event
i think
๐ You've made making a noise part of buying something. You've related them. But think of them as two different actions that you may or may not want to occur for any set of players and for any conditions. You then decide to play the sound when someone bought something but "they are not related" ๐
It's lego blocks
well, i do want the sound to play for everyone in my case
It sounded like you didn't want it to play all the sounds for every late joiner. In any case good software design is about "options" regardless of what you particular needs at the moment happen to be. Code for the future.
well, having it in that CheckBought method only plays it for in-instance people. i was thinking about a toggle to only play it locally though
i have this, and it works exactly how i want
// this is in the CheckBought method
if (globalSound)
{
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(PlaySound));
}
else
{
PlaySound();
}
if globalSound is true, it plays for everyone in-instance, and not for a late-joiner
and if it's false, it only plays the sound for the player who used the button
"Working" is not the measure of well-written software. If it doesn't work it wouldn't qualify as software. You can refactor it later if you have a mind to.
Is there any way to check to see if a player any kind of instance permissions such as the ability to kick players from the instance?
Looking for a leaderboard/scoreboard example - does not have to be across instances just for the duration of the game. My current experiment is a number of players shooting at targets and I just want to display the top, say 3, players on a leaderboard. I've been searching discord and the interwebs and have not been able to find anything useful (yet). Any pointers would be appreciated - I'm OK with either Udon Graph or UdonSharp examples. Thanks!
PShooters Prefab by MMMaleon has a scorebaord system built into it, its in the VRC Prefabs repository
On a different note, is there a way to check udon overhead on a world? I used build and test and there is a big delay on some things, but I don't know if its the world or my PC as I've been having memry related issues
Thanks for that! I completely forgot about vrcprefabs as a resource - lots of studying to do.
i had a friend tell me about some sort of replay system a couple days ago. he said it records every udon event, and when a player joins, it will replay each event in order so they everything gets synced properly. does anyone know what he was talking about, or have extra info about it?
Funnily enough that is exactly how networking worked before Udon.
And there is a reason they changed it to the current system. That way of doing things is prone to errors and weird edge cases.
im trying to make a day night cycle and most of it is already done, just having issues with applying the quaternion parameter to late joiners
this setup does serialize the value but applies it to the master/other players instead of the player that just joined
or at least thats what it did, now it doesnt do anything
request serialization is called by object owner, other players recieve ondeserialization event
is it animating itself somehow then and you need to set rotation only once?
exactly
well as a simple getaway you can onplayerjoin - local player is Master-set rotation (variable)-request serialization, on deserialization - set obj rotation
this will update rotation for everyone when someone joins
like this?
this is how i would do something like that.
you dont need request serialization at all if you set the variable to continuous sync
you only have the master actually do the rotation, then whenever the data comes over the network you rotate the cube for everyone else
Also I would use FixedUpdate instead of LateUpdate, just because fixed update will ensure that it will always rotate at the same rate
but yeah the biggest issue with this method is that OnDerisalization is called only every second or so with continuous sync, so its kind of jittery
Yeah but also calling RequestSerialization every LateUpdate isn't a great idea.
That's alot of networking data.
I figured out a solution anyways.
Use a Master Variable and Local Variable. If the local variable gets too out of sync, then force it to sync.
in the example here, if the sun is 3 degrees off from the master, than it syncs the rotation to the master variable.
genius
thanks both of you
only needed to know how to sync the rotation
even if it were just an approximation
because it rotates so slowly
Unfortunately networking is hard ๐
but this is even better
checking if the rotation is too far off, then syncing if it is
actually cool
thanks again
this is with sync set to continuous right?
yes
alright
oh yeah @nimble prairie the thing i made wasn't properly syncing with late joiners. i removed this and suddenly it worked so if you have issues i would try that
ahh good catch
whole point was to to run it offline after initial sync 
not like the sun is going to move faster or change direction
i mean thats basically what that does
it hypothically only ever syncs once
then from there it should never need to do it again
there is probably still a better way of doing it but regardless it should still work
actually i see what you mean now
technically you're right you dont need to have a continuous sync variable
animator+setting motion time once probly most efficient, no need for an update event at all, tho there will be a small desync but for daytime should be negligible
if the update is just turning a transform its not too horrible but ultimate yeah a animator would probably be better
not only just because it would run better, but it gives you more options to do more things
it gives me the option to work with euler angles lol
might switch to that
but i dont know how to operate an animator with udon
maybe just trying stuff out will work
i usually just drive animation parameters with udon, kinda like how you would with avatars
You would make a animator variable, then use Animator.SetBool, Animator.SetFloat, or Animator.SetInt depending on the type of variable you're using
kinda like this
wait i need to use that node???
i was skipping over it all this time
because i thought it was just a bool to toggle the component
no thats done with this
this graph you proposed sometimes makes the sun skip back 1 degree every 5 minutes or so
even though its using fixed time
i ended up just rewriting it anyways, idk if it fixes that issue but it should run more effciently atleast
Here i'll just DM you the asset for it, a little easier than remaking it off a image.
yo cheers
gonna test it out later cus im online with friends in that world that uses it currently
hello I have a problem with my deck of cards, it works perfectly with one person in the instance but as soon as anyone joins the cards disappear and when you try to spawn a card they appear for a second and disappear,
im sure that this is a sync issue, but i dont know how to fix it, i have tyred adding in RequestSerialization node at the end but that's about it..
any help would be greatly appreciated!
do you think normalized total seconds from the utc time of day would be a good idea to always precisely sync the sun using lerp?
Guys, can you help me please? How can it be done in such a way that when a player walks through a fiery anomaly, it starts to ignite and at the same time all players see the player burning. What do I need to do? ๐
I have a different type of trigger
@nimble prairie using utc would make it precise only for 3% of world's population tho
Ok so, how would I go about telling the owner of an object to update it's value? Working on an FPS world and I'm a bit stumped on how to properly handle players damaging each other.
Players have objects assigned to them from a pool that contains the relevant values like health.
Currently, my implementation only works for if a player gets hit by a projectile on their screen. That's not ideal, I need it so the player that shot the projectile sends the hit, passing that by the owner to apply it.
In a way that allows tracking who sent the hit ideally, for stuff like scoring and killfeed.
You'll want to use an object pool.
Each player gets an object they have ownership of and they can use that object to send data to everyone else.
I'm using an object pool.