#udon-networking
1 messages · Page 15 of 1
Try turning off Allow Collision Ownership Transfer on VRCObjectSync in case something keeps trying to take ownership of it
odd naw my VRCPickup dont show anything after i did that
Hmm, if there's no other scripts on the pickup or any other scripts trying to take ownership of the pickup I'm not sure what else would cause it to lose sync like that, VRCObjectSync should just work and take care of it all
Y i don't know is odd
How can I reinstall sdk from project clean i think something got corrupted
In creator companion click Manage Project then next to Manage Packages click ... > Reinstall all packages
For good measure I'd also try removing and readding the components on the pickup
well. yea so i asked Phasedragon before and its because apprently it runs the actual c# code outside udon.
Ok thanks I do that
that to but also just in general its slow AF
wait... send custom event delayed tells VRC to run it in c#? :O
That's not what it means. The Udon code still runs inside of Udon, it's just that the code which tracks the timing is C#
ohhhh, I see
The performance benefit of SendCustomDelayed is not that it is inherently, universally faster. The benefit of it is that you can turn your loops off and on independently from the component
if you have a case where you do actually need it to run every single frame at all times, then an update is perfectly fine.
SendCustomEventDelayed is ideal for situations where you need to run per-frame occasionally, but not all the time
This is simply because the cost of the C# code keeping track of the delayed event queue is cheaper than the cost of an udonbehaviour with an update that immediately checks a condition and returns.
@frozen igloo question then. would the SendCustomEventDelayedFrames be more optimal to run then PostLateUpdate for instance? say every frame?
It'd be no different compared to SendCustomEventDelayedFrames vs Update, PostLateUpdate just fires at a later point in the frame compared to Update.
If you're always running it every frame use the built in events otherwise use SendCustomEventDelayedFrames/Seconds to start and stop the event handling on demand
Frames will vary in time between execution based on the clients frame rate.
This is rarely desired for a low rate update.
Also would be interesting to know how time accurate running a delayed seconds loop is.
If you need time accuracy, doing a seconds countdown in Update/FixedUpdate would probably be more preferred
Like a
_nextUpdate -= Time.deltaTime;
if (_nextUpdate > 0) return;
_nextUpdate += _updateRate;
Delayed custom events run in sync with their managed event counterparts so SendCustomEventDelayedFrames will skip X number of Update/LateUpdate events before being called and SendCustomEventDelayedSeconds will skip at least X seconds worth of Update/LateUpdate events before being called
SendCustomEventDelayedFrames/Seconds at 0 will always run on the next frame so you can still use Time.deltaTime there
I've only ever found delayed frames useful when I need something to complete before I take another action so usually a frame count of 2 for me.
Doing an update loop with it > 0 just sound like headache
Also what phase in unitys update cycle do these calls run in? Because there are some pretty major issues with a few vrcapi calls I've run into that depend on if your in Update or LateUpdate
delayed events allow you to select timing
Event handling is managed by the UdonManager under the hood, it basically looks like:
public class UdonManager : MonoBehaviour
{
private void Update()
{
// Update elapsed time/frames since last update
// Call Update() for all active UdonBehaviours that have one
// Update delay event queue for UdonBehaviours
// Run scheduled delay events marked with EventTiming.Update for UdonBehaviours
}
private void LateUpdate()
{
// Call LateUpdate() for all active UdonBehaviours that have one
// Update delay event queue for UdonBehaviours
// Run scheduled delay events marked with EventTiming.LateUpdate for UdonBehaviours
}
}
Ah so the phase can be selected, I have not paid enough attention to the delayed event calls signatures
Does a change in ownership require a request serialization after?
I always assumed ownership was inherently networked, but it seems like multiple people can recieve True isOwner at once
it is networked and no. it may just take abit for it to change.
"it seems" as in you have code that demonstrates it or as in you heard someone say they heard from someone?
When a client assumes ownership, isOwner will be immediately true for them for the time being.
However transporting this new ownership state over the network takes time.
During this time, someone else might try to assume ownership as well.
In the end the last person to assume ownership will win and tell everyone else that they are owner, upon which everyone else will then see isOwner as false.
So yeah momentarily isOwner can be true on multiple clients.
This is inherently due to VRChats practically-serverless networking architecture, which only guarantees eventual state synchronization, which means that after an indefinite amount of time without any changes to networked state, the local (networked) state will be the same on all connected clients. Usually this only takes a few hundred milliseconds at worst, but especially when networked state is causing other networked state to change, this can quickly get you into hard to handle cases and generally "network unrest".
Thanks for taking the time to spell this one out for me ❤️ appreciate your help
is odd but whin items get spawned in world VRC pickup brake is keep teleporting but if item is in the world from start is ok
did i found bug ?
are you using Instantiate to spawn the object?
im not that grate in udon so most of the time im doing error tell somthing works
@obsidian python
what othere type is there ?
is spawns object on button press
did you write the script?
depends on how many objects you want to spawn, if you need a lot, then you need an object pool
or only way do it is to telport items from corner of the world?
is only spawns 2 ojects for naw even 1
if you want it to be synced, you would need to have multiple of them disabled when the game starts, and when someone presses the button, you would enable them one by one. or just use an object pool for that, there are tutorials on youtube how to do that
i se so the have to be in world that is my problem @obsidian python
every object that you want it to be synced, they must own a network ID. what you spawn during run time with Instantiate, they won't get any network ID, so they won't be synced over the network
@obsidian python thanks naw that i know that i can get it working i was at it days naw
so over the past year or so, my community has reported a somewhat rare issue that sometimes causes pickups to slowly float endlessly off into the distance (in one random direction), usually after a U# script teleports the pickup's Rigidbody (though I'm not sure if that's a requirement/cause). i've looked through my code dozens of times, and i can't imagine any way that any of my scripts could possibly cause this, so i'm inclined to believe this is a VRCObjectSync/VRChat networking issue
but given that: could this be due to network clogging? i have about 625-650 instances of VRCObjectSync in the world, but otherwise, every other script is synced manually (only syncing mere bytes at any given time, and not very often)
if so: does anyone know of any ways to mitigate this issue, or mitigate network clogging when using VRCObjectSync, etc.?
if not: does anyone know what else could be causing that issue?
Do the rigidbodies have no gravity, no drag, are not kinematic, but are also not expected to have velocity normally? If so, then it's possible that even if the owner doesn't set velocity, it's possible for someone to take ownership during a lerp and velocity can be introduced by the network code. Then if you never clear that velocity, it will just slowly drift off.
The best way to fix this is to just add drag, gravity, or set it to kinematic. I'm not aware of any use cases where you'd legitimately need it to have all those things in one and also not be manually controlling the velocity
the pickups are all kinematic, and do not have gravity. this has always been the case. they should only move when teleported by a script, or while held by a player
in case it's helpful, here's every component on one of the pickups (they're all identical)
I had the same issue and it drove me insane lol
There definitely is something up with how VRCObjectSync syncs it's state. It usually happens when you disable them and then enable them again in like a pooling system or something. The state gets auto update a bit later and not directly when you spawn it.
I think I did a bunch of SetGravity/Kinematic calls on enable and then also OnDrop just in case.
yes, these pickups used to be disabled & re-enabled as players interacted with menus in the world, but recently I had to modify all the world's code to keep all pickups active 24/7 and hide the relevant meshes instead, since VRChat broke that old system upon releasing persistence. i'm not sure if that fixed it though, as the last report of the "floating pickups" bug was 4 days before i made that change
what like, kinematic = false; kinematic = true; ?
i remember trying to call rigidbody.Sleep() OnDrop and whenever they're teleported, but I remember this breaking each VRCPickup for some reason... maybe i should try that again now that i know what i'm doing, lol
VRCObjectSync.SetGravity/Kinematic is what I use, not sure if they are interchangable. I basically set for the master on Start, when first deployed from the pool and then every time it gets summoned. Might be a bit overkill but it worked.
Ah and some objects OnDrop
hmm, i suppose i could try that. i ignored those functions before bc i assumed setting them on the rigidbody would suffice ofc, but yea maybe that'll work idk
Pretty sure this is it? https://feedback.vrchat.com/udon/p/vrcobjectsync-doesnt-sync-gravitykinematic-flags-reliably
Ahh, this one is more accurate I think: https://feedback.vrchat.com/udon/p/object-sync-problems
yup, that's the bug that got wAy worse after the persistence update
so then maybe my issue will fix itself now that the pickups stay active?
idk, but i can try the VRCObjectSync methods u mentioned next time the issue gets reported tho, as it's always been v rare. so thank you for that suggestion !
but i do have one last question: i'm planning to eventually increase the pickup count to around 1,300-1,400. do you think that'll be an issue in terms of network clogging, etc. ?
traffic is limited by the emitter ie owner of object
with one person owning every pickup its 200 being unreliable shit already
a person owning 200 objects is nothing lol and it wont be unrealiable unless you have it spam sending network events every single frame.
The object sync component intrinsically comes with a necessity for continuous sync if you want to place any UdonBehaviours on it. Even if no UdonBehaviours, the networking I/O is still there from constantly sending its transform and rigidbody information
Even in less pickup heavy worlds, if they all are owned by a user on a mobile platform, the networking strain can be immense
That's mostly true, the exception is that objectsync has a special ability to go to sleep. If it's not moving, it's not transmitting
As a result, just owning 200 objects is not unrealistic. But constantly moving them is
This exact situation is part of why the server buffers data for late joiners, so that the owner doesn't need to resend every time someone joins (that used to be the case, and it sucked)
At 200+ non moving pickups, I'd start to question design decisions, but yeah
200 objects in a pile were causing suffering on test client. its 5hz, not every frame.
Being in a piles makes it much harder for them to fall asleep. Any little jostling will wake a whole bunch. It doesn't make it impossible for them to fall asleep though if it's a small pile
For one of my testing processes, I made a little script that sends a burst of particles when data is sent. It really helps to visualize what's happening. If that's a problem you're trying to solve, you could use a script like that to point you in the right direction and identify cases where they're not falling asleep when you want them to
Similarly, changing color on ownership transfer is a good way to visualize how ownership spreads when objects touch each other
i tested with like 5 pickups laying on the floor for ages, never went below 5hz
@frozen igloo would you per chance know how to check if udon is slowing down alot? yesterday i had a map test. and i noticed it started slowing down udon so much that it skipped alot of networking events and i had yet to hit the 11 kb / s limit. could i have run into a rate limit of networking events being send?
the only reason for network events to be thrown out is if they are unable to send after 120 seconds, which only happens under extreme load. In my testing, that starts to happen at about 6000 network events sent at once, but please don't rely on that number to be precise
@frozen igloo hmm. so the reason why is because yesterday i had about 35 people join in. some later then others and i have a rejoin functionality that sends the data from the current master to everyone. this happens once a minut and checks if its needed etc. however smaller network events were send to each client every time someone would join but delayed 10 frames. and this all worked fine until it hit 30+ people.
then it started simply skipping various methods and such on the master side.
oh and these packs of data would vary in size from few bytes to 500 bytes. for sending resync data.
hmm wait since i am using @obsidian cedar NetworkedEventCaller which has that build in rate limit of 3 per second per client.
Yeah sounds like what you have is a bit too complicated for me to know for certain what the problem is
all i really sorta have found it is that it could be. considering the master was sending more then 3 per second i dont know if it actually discards them if over
It shouldn't discard anything, Manual sync SHOULD be reliable
hmm.
And anything you queue up in NetCaller should also not discard
it should get queued up and get sent in next NetCaller "packet"
hmm okay. interesting
but maybe i had it overloaded? and it just kept adding packages?
ahh. its hard to know ngl since all i know everything either stopped sending or got heavily delayed or skipped
if this is going through manual sync then no, intermediate state is not guaranteed, only the end state. This is because when you change the synced state, it will interrupt and overwrite whatever was already in the middle of sending
That said, I assume net event caller has some sort of functionality to counter this?
hmm. so my best bet is to assume it only sends things at 3 per second to avoid any major delay? or potential data overwriting if thats the case
I am not remotely familiar with the specs and limitations of net event caller
@obsidian cedar maybe you are able to enlighten us abit 😄
It doesn't have any functionality to counter this, it was planned but never finished 😅 I got fed up with Udon and stopped working on it, might finish it eventually
Maybe when Sona releases
yeah that sounds like what you're running into then 
Though I do swear I heard someone say Manual Sync was reliable and should make sure all syncs are delivered 
that sounds like either a misunderstanding or someone uninformed
manual sync has never been reliable for intermediate states
it has always been eventually reliable which means it's reliable that everyone eventually ends up on the same state
old states are immediately thrown away
See convo here -> #1001561469932019742 message Maybe I missunderstood
that's a pretty big convo, but yeah when Euan says reliable he means eventually reliable, not every intermediate state reliable
@frozen igloo i assume the traditional way has a way to counter act that? like the package overwriting
Could you elaborate on what you're asking?
not sure what you mean by traditional way or package overwriting
well i mean not using the thing miner has but the Request serialization and other things.
afaik miner's thing uses manual sync/requestserialization which is what this is all about
though if you mean actual network events, then see this response, which is what I thought you were asking about
i mean this one.
miner's thing uses manual sync so that response was referring primarily to miner's thing
seems to be a pretty big limitation if it's not accounted for
hmm.
right so tell me again what can happen if i say send 10 networked events at once from the client. could it potentially interrupt it? if its all within say 10 frames
like 1 per frame etc
Please clarify if you are referring to vrc network events or net event caller events
yea sorry vrc network events
10 vrc network events is barely anything, they're able to handle that no problem
and network events don't have the interrupt problem, it's different
okay so i missunderstood that part. what exactly can cause interruptions then?
Manual sync gets interrupted if you resend in the middle of something being sent out.
vrc network events get thrown out after 120 seconds (ish) of being unable to send
manual sync does not have the timeout problem, that's why the term "reliable" and "eventual consistency" is used - no matter how long you're clogged for, it will eventually get out
and likewise, network events do not have the interruption problem, so they're very good at sending bursts. They only start to run into problems when you get into the thousands of events at a time
just remember that miner's net event caller is using manual sync
yep yep
so it is subject to the interruption problem
of course, that's the primary reason why I'm so adamant about telling people to use view-model architecture, let the server handle late join sync, and to avoid sending multiple different sets of data in serial on the same object
in the server case here i assume u mean just letting the vrc events handle it self?
not events, manual sync
all data in manual sync is buffered on the server and replayed to late joiners when they arrive, in a manner that is far, far cheaper than the owner resending the data
I highly recommend going with the flow of this rather than trying to build your own networking infrastructure that resends the current state to players as they join. Because that will always be subject to much, much harsher penalties
NoVariableSync is just an udonsharp concept that disables udonsync variables without actually setting it to nonesync
does it still receive the late joiner thing?
I'm not sure how that's relevant to your situation though
afaik only network events will work on NoVariableSync and in that case no, because network events are not buffered
that, and design your code to use view-model architecture
which means to treat the synced variables as the "model" which fully and completely represents the state of the world, (and not as intermediate data) and then your code simply takes that state and applies it to the "view" which is what the player can see in the world
oh yea i know MVVM just never really thought about being able to use it that well in a udon situation
the moment you start sending chunks of data at a time through the same object and then piecing them together on the other end until you have the final result, that's breaking this concept and will not work well in vrchat
Manual sync gets interrupted if you resend in the middle of something being sent out.
and that is why we need an event that tells us it's been fully sent out 😆 Unless I'm mistaken there's nothing like that still
I mean that would be helpful to some degree, but that kind of event would just encourage more of doing this exact thing which is what's causing the problem in the first place
Well then we need SendCustomNetworkEvent with parameters official implementation 😆
that would really be nice
yeah, the barriers towards doing that are relaxing. It'll happen eventually
If Sona doesn't have it on release I'll be lil sad tbh but sadly expecting that it won't 😅
yea currently the only thing i am doing is creating a custom rejoin thing. but i am guessing i really should just rely on Late joiners buffers instead
but of course, people who want to use it precisely for the purpose of sending late joiner data on join are one of the big reasons for resistance against net events with params 
You shouldn't be using NetCaller for late-joiner data 😅
Unless you know very very very very well what you're doing and why you're doing it xD
There ARE cases where you want to do it
But it's extreme cases and super rare
yea no i found out that the hard way now lol.
thats why i said i prob should instead of sending out the late joiner data nad reassemable it i just need to rely on the one vrc has
NetCaller is basically RPC system and it shouldn't reallly store state
👌
its prob also gonna solve all my problems regarding rejoin lol.
Just have a Manually Synced Variable that syncs the late joiner data
yee
good thing i am doing object based ownership. so it should be fairly easy to do.
Yes, having that attitude is exactly why I'm not abhorrently against the entire concept of net event caller 
@frozen igloo thanks for your time mate it diff did enlighten some of the issues i have been having.
oh and i found out looking through some of my code i am actually doing MVVM already. just not with the late joiner thing.
or somewhat

@frozen igloo abit of a wierd thing but eh. OnDeserialization is not being called regardless of what i do. even when i do request a serialization
add onpostserialization and log result success
if something is preventing it from sending, ondeserialization won't happen and onpostserialization will say it failed
okay gonna try that
so uhm. @frozen igloo OnPostSerialization does not even run either.
and i did a very very simple test. that just has a string that gets set on a button click and then RequestSerialization
only reason that would happen is if networking is fully disabled on that object, one way or another. Is it instantiated? What sync mode is it?
is there way syc all items in world so new players can se whin the join changes?
what old players did
it depends on what system you're referring to. Udon sync'd variables are automatically sent to late joiners, but this is the last value. If you need a queue of changes to apply, you'll have to make that system yourself
i wanna on new player join all objects show location and state like old users se like most wolrlds have
i'm having some issues serializing some specific data, when a player leaves an instance, all the GameObjects that were under his ownership are supposed to go to the master, does that happen before or after the OnPlayerLeft() event? or do i specifically need to call a RequestSerialization() event for that change to happen?
You may want to use OnOwnershipTransferred instead of OnPlayerLeft, also the leaving player cannot serialized once they start leaving
Though i dont know if it happens before or after when using OnPlayerLeft
cant use on ownership transfer because of some other things that i do in the same code, but after testing a bit i figured it out, ownership transfer happens before OnPlayerLeft and... this seems counter intuitive to me but PlayerObject destruction happens after OnPlayerLeft... which means that there are objects of the player that left after all of his ownership from other objects have been transfered
i found the reason i was being a dum dum
@frozen igloo so i found something odd. i have some objects that follow a player around when they are the owner. however i found now that when i set the owner to an object it just makes that object dissapear even when i dont set its position. and i also checked if the position is different from what another person sees and its not.
what are you using to attach the object to the players?
GetTrackingData for remote players just returns the bone pos/rot, not the viewpoint like it does for the local client.
So on avi's that have not normal rotations, the object can rotate into the body instead of following.
Also, while I don't remember what it was and the nuances, but I had a lot of issues with TrackingData or Bone positions being wildly different depending on which function in the update cycle I called the functions, like Update vs LateUpdate gave me some big differences
(source, made a selectable achievement hat system)
using? i am just getting the Tracking Data. and setting it to the Head bone position, rotation and such.
and even without modifiying it. the object would just disappear. oh and i use PostLateUpdate for it. since thats accurate to the latest position
tracking data or bone position?
well Bone Position for the head.
bone position is 0 for non humanoids and shouldnt be used
its fine its a humannoid in this case
tracking data is camera locally but head bone remotely
(remote clients cant look too much up or down)
again even without this. it would for some reason. when a player join that gets ownership of the object make the object that follows u just pooof
you want to use tracking data anyway when appliable
poofs how, where it goes, make it log position or smth or test it with multiple clients in editor
i did log it and it goes to the right positions etc.
but it just no longer renders for the local person
but other clients can see it
@tulip sphinx i found out something more. apprently when i am doing a RequestSerialization it for some reason causes the object that follows the other person around to just disappear
does the RequestSerialization block behaviours from updating? while its being deserialized?
No, it's like setting a dirty flag on the behaviour so it knows to serialize on the next network tick, it doesn't serialize immediately when you call it
i know. but for some reason it seems to cause the object that follows a person before it happens to just disappear from the other persons view. even through it do nothing to those objects apart from updating their positions based on a persons current position
and its all local
update. i found out why. i had forgotten i had left a OnDeserialization in another behaviour and it was triggering that. which causes things to go invisible if something is not allowed to be seen.
do i need 1 SynchronizedItems script in world for othere scripts that have items disabled and if players spawn them new players cant see item or should i make so all the buttons have SynchronizedItem code in them?
need some outside eyes to look at this shit, its ment to toggle an animation on when a player is inside a box, and then when they leave it toggles off using the "PlayerInside" bool. The toggle on once in the box works but it dosent flip off once the player leaves
why do you need the self replicating loop? Can you not just:
OnTriggerEnter -> SendCustomNetworkEvent("DoorOn")
OnTriggerExit -> SendCustomNetworkEvent("DoorOff")
DoorOn -> SetAnimatorBool(true)
DoorOff -> SetAnimatorBool(false)
Also this doesn't account for if multiple people walk into the trigger, Ideally you should keep a count of players that enter and players that leave and only disable animation when num players == 0
It also shouldn't need to be networked at all because any player that enters a trigger will trigger it regardless of client
OnTriggerEnter -> Increment Player count -> SetAnimatorBool(true)
OnTriggerExit -> Decrement Player count -> if player count == 0 then SetAnimatorBool(false)
where exactly can i find the Increment Player count node? or is there another one i have to use
Hi. I am using SendCustomEventDelayedSeconds to run some logic. The SendCustomEventDelayedSeconds is called only by the owner of an object, and the resulting event is sent to all players.
If the owner of the object / function calling player leaves the instance, after SendCustomEventDelayedSeconds has been called, but before the delay time has passed, will the Event still be sent?
no
Cool, thank you!
question: how can i make so that something automatically enables itself locally after an user has been inside an instance for 1h?
Start - Sendcustomeventdelayedseconds 3600🤷♂️
I’m trying to get objects to sync globally but anything I try just doesn’t work even the synced toggles they give you doesn’t work for me.
show smth
this is what im currently trying to use
obj on set owner should be empty ie This, object containing script/synced value, not target for toggle
statevar change should be replaced with on deserialization and also after request serialization connect to same set active (or rather use separate event for it)
ie request serialization - event, on deserialization - event, event - set active. thatd be more scalable
var change event should work but thats again not uh primary method
so something like this
like this, and it's done? o.o
that was way easier than i expeted tysm
anyone played around with the input system in udon? i can't seem to find any documentation how to do something on for instance the B button with a vr controller
iwish that was useful but it doesn't provide anything
ok say that we have found the B button in that spreadsheet. What would you like to do with it?
eh just something simple like bringing up the HUD for a player. that isnt on all the time
you have two options for the input here, the B button as mentioned where you will have to poll this in an update loop OR use one of Udon's input events without polling in an update loop. For example: Jump, Use, Grab, Drop
feels more like a game logic question than an udon networking question at the moment. Have you tried to break down the steps required to bring up the said HUD?
yea i know. ngl i was tired the first time i was reading it. found it now lol.
Is it normal for SendCustomNetworkEvent(NetworkEventTarget.All... to most of the time take about two seconds to reach the other client. If i change a variable and do a RequestSerialization , OnDeserialization() is called "immediately" and i can print the changed value. Testing is done with Build & Test New Build in Unity.
Surprised you didn't get a bunch of replies by now. No there is no "normal" time and you shouldn't rely on some arbitrary passing of time if the system relies on it. It won't work in the long term.
No I'm not relying on it for time, I'm wondering why it's so extremely slow compared to updating a variable.
how would i do a synced instigate?
Take two what? 🥺
Oops, seconds
That's so quick 🌷
Think of it going out into the internet, getting ready to shoot at everyone, and firing off ⭐
Networked events are more about "let's try to do this together" instead of "let's try to do this asap"
I'm not sure what your mean by let's do this together since it will call the method instantly on the client that fires it and 2 seconds later in my second widow.
how do i take a players name saved in a string to get their position cause i cant plug those two together
It just seems like incrementing a variable and then calling a method in Deserialize is a better way of doing events since it's so much faster and it does not make sense to me.
It shouldn't be taking several seconds for a network event to get through to your other client unless you've got a really bad network connection
nvm figured it out
It says network events aren't the fastest in the docs, that's just how they are
Like you said there are other ways if you really want something to be fast fast
It is a variable amount of time which is why it shouldn't be relied on in combination with a sync request.
Hmm i thought i read the exact opposite that syncing variables was slower, but your text there clearly contradicts that idea.
Manual sync is definitely the fastest option
But several seconds for network events is abnormal
They don't exist to service "speed". They are used for different things. A network event is fundamentally a RPC (remote procedure call) in Photon.
Have you tried it with a published build to make sure it's not just an issue with build and test?
It doesn't represent the true networking environment
Oh, i have not. I will try that out when I'm back at my computer and see if there is a difference.
In any case you should decide what you need to do and use the option that provides that solution. They are not interchangeable.
Question. Is it better to have several smaller Request Serialization over different objects or have one object that handles it per person? the reason why im asking is cause it does seem to Serialize all UdonSynced Variables in the same object regardless of it being changed or not.
hmm. seems like Every udon behaviour has a overhead cost of 12 bytes when being networked.
So i got a script, i know for a fact that the owner sets a variable, and then calls request serialization
It does not call OnVarChange on remote users for some reason, but it does on the local player, any idea why?
It is set to manual sync
Are objects enabled at all times? Do you initialize the synced vars? I remember especially arrays can bug out if you don't. Not sure if it's still the same.
Yep, if you have frequently changing data, then it's best to not bundle it with other stuff and make your own object for it.
yep, the object the script is on is enabled, and the value of the synced booll is set to true beforehand.
Its when its set to false that it doesnt work on remote players
the onvarchange does work when im setting it to true
Try printing it in OnDeserialization instead
So OnVarChanged does trigger, but just not when you sync it as false?
I'd be curious how other values like ints would respond...
ill try, thank you
correct
it usually works on other scripts, dunno why this one doesnt want to
well, its like remote player does not fire ondeserialization
but the local player does
did u mark the Variable UdonSynced? and are you sure the one calling it is the owner of the object? cause it wont fire OnDeserialize for the local player. only others
this is specifically for an udon graph, and yes i did the equivalent
then i got this mixed up
maybe its a graph bug
Okay so i dont know if this is related at all, but after a bit of testing, the behavior im seeing is:
Only on remote:
- When set to true, itll go through the Onvarchange false route first, then the true route for some reason (when it should be only true)
- When set to false, nothing
@obtuse echo @finite sierra
this is the first time im seeing something like this, other tests have been pretty inconclusive and i usually find the answer by then
uh it diff should show for remotes.
hmm, maybe im calling serialization too early, and it confuses it.
Ill do some more testing
You set owner, change the value and then request serialization. It should work if you do it that way. If not, then you probably should post code
i did, doesnt work. Im just gonna send a custom network event that sets the value on remote at this point.
I could post the code but it is a pretty big graph, doubt youd want to look through all of it haha
although i could post only the relevant parts if you want
the following c# seems to be not exposed to udon. does anyone know what alturnate function I should use instead to do the same thing?
Networking.RPC(VRC.SDKBase.RPC.Destination.Owner, gameObject, "RequestHostAddPlayer", Networking.LocalPlayer);
I would like to make a non owner of the object. Send a paramater to the owner over the network to be used in a function.
You cannot do this. You must have the non owner affect something they do own that will only trigger for your desired target
Such as modify a variable indicating the owner's ID
But in this case, it seems like you can just have a bool for like requesting authorization and if there's only one authority deciding, then they automatically know who they are
ty
does anyone know if the World Debug panels aka Panel 6 shows total In & out data for the instance or does it show per player basis?.. the reason why im asking is because if i check it on multiple players it shows the exact numbers going in and out. Even when a request serialization happens for another person it goes up equally on all World Debug panel 6. If its per player basis something very odd is going on as that data really shouldn't go up unless x person n request serialization
anyone know what box to set a objects material with? like, on interact i want this objects material to change to another mat?
oh that's a fun one
you need to get the object's MeshRenderer, then grab its Material from there
presumably with that you could set it to another material. Most I've done is change the color
thank you!
okay, quick question, how would one go about stopping a sendcustomeventdelayedseconds? like i want people to stay within a zone, and if they leave it resets the progress?
I think there's no stopping the train of a delayed event, but in said event you could add a check at the start to determine if it should actually run the rest of it
so 2 seconds later, check if players are outside the zone, if so don't run the rest
ya dont. just set some bool to true when queueing event, false when executing or exiting a zone and check if its still true in event itself.
I have 2 int arrays both udon synched.
Array a contains between 0 to 5 elements. Each time the object owner modifies them, a new array is constructed and replaces the old one. The size usually changes by plus or minus 1 each time it is modified
Array b works the same way except it is always size 52.
Array a works as expected and properly synces to all clients. However array b will never cause sterilization when modified. Evan using manual synching mode
I am trying to find possible reasons for this issue. Is 52 elements past the limit for udon sync? Or will and update not be allowed if the size of the array stays the same. Or is there some other limitations I don't know about
I think that would be over the serialization limit
array b should be syncing as well. are you sure you aren't simply missing the deserialization for that array?
52 elements for an int array shouldn't be over the limit. I have a test world for a project I am working on that routinely syncs int arrays hundreds of elements long. although it doesn't do it often, so the network has enough time to process all of it.
Is it using continuous sync?
no, manual sync
triggered by a button that will initiate procedural generation of part of the terrain (and that terrain should be the same for all players, so the relevant data is synced over two arrays that are usually 400 elements long
Nither arrays have any code on that call back. There is just a function that runs to print the contents of both arrays into text for debugging.
Am I supposed to set the value of a and b to themselves inside of ondesrialzation?
no, that wouldn't accomplish anything
is your udonbehaviour using manual or continuous synchronization?
I've got a version that's manual with the request lines added and one that's continues but they both behave the same
Well continuous is maxed out at 200 bytes per serialization which an int[52] would be over for sure
Yea so manual will be the one i should move forward with for sure
manual sync should work. although https://creators.vrchat.com/worlds/udon/networking/network-details gives conflicting information there (it claims the limit is 64690 bytes, and later claims it is 280496 bytes), 52 ints should be within both of these limits
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
Are these limits shared between scripts?
on the same object, yes, on different objects, no
on the same object, the most restrictive limit applies
so if you have 1 continuous and 1 manual on the same object, the manual can only do 200 bytes per sync
What it's 2 objects but one is a child of the other
that should be fine then
but do check if removing the continuous one helps, as it may be saturating the shared 11kb/s limit
And let's say I have 7 synched vars is there a way to request sterilization just for one specific value?
I'll try that later. Away fron my machine at the moment just trying to gather info until I get another chance to trouble shoot
no, you can only request synchronization of all variables on a behaviour.
if you absolutely need to sync them separately, you should separate them into different behaviours
If that's the case I think i will. I'm making a card game that has the potential to move quite quickly so it may start pushing limits if I end up syncing stuff that does not change
ah, so the 52 element array is probably the deck order then
Yup
in that case it may help to change from int to byte.
still able to encode which card it is in 1 byte, but the number of bytes needed is reduced by 75%
Well there's another array that's not synced that contains the actual 52 game objects, for the cards themselves.
I sync the ints then the client aranges those game objects according to the int arrays
I'm not sure they could be used to target the index or an array if they are represented as bytes
there are implicit conversions between byte and int
If that's the case it sounds like a worth while optimization
As long as i never need more then 255 cards it should work
256 if you don't need to represent the lack of a card
I think I'm just going to opt to resize the array as cards are removed from the deck and given to players
If you don't need to put cards back into the deck mid game you can sync a seed value for the random number generator to use for shuffling an unshuffled card deck along with the number of cards drawn from the top of the deck to reproduce the same deck state for everyone, and it would only take like 64 bits to sync everything
assuming you can guarantee random numbers will always be drawn in the same order
So long as you're using Unity's random generator it's fine, it's always running in serial on the main thread and you set the seed
UnityEngine.Random.InitState(Seed);
Utilities.ShuffleArray(shuffledCards);
ShuffleArray uses UnityEngine.Random.value
if you only need it to be like that in a single behaviour for a single frame, that will work.
if you need it to be synchronized across multiple frames, good luck
You only need to shuffle the array on deserialization
And only when the seed changes
I think I'll pass on that optimization and maybe revisit that idea once the game is working at a baseline
The byte conversion is less complicated to do for rn
But the deck is probably going to be drawn from several times every 5 to 20 seconds
It's mostly gona depend on how fast players play on average
also keep in mind that if multiple players are drawing, the order in which they end up drawing can be different for each player, as the local player will have the least "lag", as no networking is involved there
Yea also the game allows for players to make moves while it's not their turn witch is why I want the deck owned by a single player who manages it
The logic for that is going to be fun to build lol I just hope I don't have to split it into to many scripts and objects
Right now thinking of having each player own an input queue. Anytime someone updates their input queue the session owner checks it and prosses the change request
that is actually a decent application for a playerobject. each player automatically gets their own instance of it. the master script can just iterate over all instances and check the queues
That's the idea
Ideally the master also holds all the sync arrays for each players hand too
That's also why I'd want to resize the array for the deck. Because there's no limit to how many of those cards end up in a players hand at one time
This way the total number of elements across all arrays is always 52. No need to sync empty values
But in anycase thanks for all the info I think I'll be on the right track to fixing it once I get home and continue with it
Although I may look into pre existing objects instead of a player object it may make the game more asset friendly if i build it in such a manor that allows it to be easily dropped in other worlds
Like the pool table prefabs etc
why would playerobjects not be asset friendly?
Arnt there limits to how many you can have per player?
the sdk documentation doesn't state anything regarding that, so the basic assumption is that there is no enforced limit.
the usual networking and persistence limits will still apply though
its also out of date. there are several things that are not correct anymore. like the 11 kb limit per second is more like 6 or 7 kb/s. plus anything on your end like Face tracking, body trackers etc count towards that max. i know this because i have both and face tracker alone cost 2-3 kb/s to just use
so avatar parameter sync affects the bandwidth available to udonbehaviour sync?
yep. but only your own.
ye. but its outgoing bandwidth so the amount of players doesnt matter. and you must own all these networked object as well ie around 250 vrc sync pickups for example
untill it starts to suffer and lowers sync rate below 5hz
see thats where i think something odd is going on. and the reason why i am saying that is because you can see your Out Data going up when more people join. for instance if i am one person in my world on desktop i use roughly 0.2-0.3 kb/s but as soon as a friend of mine join and we do nothing it jumps to roughly 1-1.3 kb/s for no reason.
and a map test i had this sunday i reached 38 people. no issues but i saw data out on my end roughly going between 8-11 kb /s it would reach 11 if i start talking cause of face tracking.
but i would also not get any network is clogged problems.
so either the Master in the world has the total data going out for the world. or something else is going on
@frozen igloo are you potentially able to explain that behaviour? seeing you have abit more knowledge then most in here about udon networking
Everyone has both out and in numbers. Out is entirely individual, determined by what objects you own and what data you're serializing (as well as avatar params). This number is where limitations are primarily applied.
But then each person also has data in. This is representative of what you're receiving, which is typically (but not always) a look at what's happening in the instance in general, what other people are doing. This has less server side restrictions, but it can still run into bottlenecks based on your connection if it's particularly severe.
Perhaps you're mixing up the two numbers? Or some other one? You should clarify which number you're looking at, with a picture ideally.
@frozen igloo no im looking Specificly at Both In and Out. In reaches as high as 150 kb/s Ingest when there are alot of people. aka 38. and i am looking at the World Debug Panel 6 on my own end. However i also noticed that the Data Out shown top left with that Data in. Shows 8-11 kb/s with 38 people.
specificly each person has a max serialization per second of 0.1 kb/s done manually.
roughly speaking each person request a serialize possibly 3 times or so every so often and going as low as 1.
and each person owns one Object with a bunch of behaviours on them but only one has manual the rest is no variable sync
so my question really still stands how come it is as high as 8-11 kb/s on my end as the master when there is 38 people and if i am alone it only hits 0.3-0.5. kb/s while in desktop that is
oh and each person has 4 Variables UdonSynced, that are byte,short,short,byte so 6 bytes in total. but obviously udon overhead is quite high so each object is roughly 0.056 kb
If you're alone, a lot of the internal stuff like player position and ik doesn't run to save bandwidth
right. so every person that joins does count on each local persons end to? or hoow am i to understand that
and also if i understand it correctly that means if you have 80 people it should go quite high on out?
You're probably only sending out your own position and ik once to the vrc server so an increase in other people joining the world will likely have little to no effect on your out. The VRC servers likely handle the rest.
In theory that should be true, but of course if you implement things like onplayerjoin resync then that gets thrown off
well i am using normal vrc stuff now. so and i am calling the Get Tracking Data to get the most up to date position of a player. in total there are 80 objects that can be owned. one per player. and when a player joins they initialize those objects with the synced data that they get from the Late join events
Hey hey, i am having a problem with a synced int. for some weird reason when changing the value and calling RequestSerialization(); the other players dont get the new value. But people that join later get the new value. The UdonSharpBehaviour is set to manual and the owner is set to the person changing the variable and calling RequestSerialization(); Are there any other requirements for RequestSerialization(); to succeed or troubleshooting steps i can try out to find the problem? Ah yeah and also the OnPostSerialization(): Methode is true for the success variable on the owner that is requesting Serialization. But the other players OnDeserialization methode is never executed. If anyone has some more steps i cloud troubleshoot please let me know. I am already struggeling for 3 days xD. I also just started with Udon and C# so could be just a simple mistake of mine that i overlooked.
Can you show your code?
Here they are. Its about the SyncedTarget variable thats not getting synced in the Mailbox.cs script. The SyncedHouseOwner variable is working tho. And the SetTarget() methode is run from the HouseInside script where the SendTarget() methode is called by a button click.
oh my god i found it ahhh xD in the Mailbox script in the end of the public int SyncedHouseOwner methode i disable the gameObject for everyone else except the local player so thats why nothing gets synced
What can happen if two players try to get ownership of a VRC Object Pool and try to use the TryToSpawn() method and spam it really hard? For me, the VRC Object Pool starts to invert the sync active state of some objects, while its active for one player is inactive for the other
Maybe it's my implementation but i'll try to do a basic approach and report back if its the same
That's what I've run into as well. VRCObjectPool just isn't reliable with competing ownership -- different users end up with different perspectives as to what is enabled and what is disabled. It comes from how ownership changing is implemented.
Thank youuuu- thought I was going insane. Guess I'll have to change some plans then
ah, should we avoid using vrcobjectpools then?
as long as you aren't going ham on ownership transfer of the pool it should be fine
A lot of ideas that VRCObjectPool used to do work better as PlayerObjects though, so I would consider using PlayerObjects first
if you want two different players to request pulling out of an object pool, one of them should stay owner, while the rest should be requesting the owner to spawn the object
i see, makes sense
Is it possible to reach across VR platforms?
no
Does "reach across" have a definition?
Any kind of transfer of data...i already know video streaming is possible...but looking to build a framework for leveraging more user data for the users...
Im VERY new so any experience or critics are welcome...
Does UdonSharp have any kind of support for WebSockets? ...or if OSC can be used in conjunction with WebSockets...?
VRChat intentionally makes it hard to send data out of the client.
If you're looking to GET data from somewhere, that's much easier.
You can do this with video players, string loaders, or image loaders.
kinda not possible with vrc. heavily limited on how much data can go out and how its being send.
I need help testing...
no, the best you can do is build your own website and have embeded commands in the link but vrchat only lets link do things so many times in a short span of time so doing something like realtime communication is not possible but slow over time communication is (wouldn't be dynamic either it would all have the be predefined stuff so you cant get someones xyz position and use it unless you defined every possible position and i dont think it'd be a good idea to do that)
If I want to make a button anyone can press to recall a sync'd pickup, I'm guessing I need to set the button presser to be the owner of the pickup too, right?
yep
hmm. what is peoples experience with the current limits of udon? in terms so of sending data and receiving? when do you see the limits being hit etc? a and how many request happens per frame and such?. the reason why im asking is because i had a successful map test just yesterday 47 people synced up well and no issues with it. Apart from 3 people that joined after roughly 35 people were in the world. and udon outright skipped a sendcustomevent. That prevented x person from being synced. and they had to rejoin to have it working.
is there an actual definitive answer as to how much data we actually have. and what exactly counts towards each person data being send out. and how its handled.
the reason being is that if a person only send out roughly 100 bytes per second. and its manually done. then that local persons data out should never go even close to 10-11 kb /s ? but in my test yesterday i often reached 10-11 Kb/s on my side while other people it would hover around 5- 7 kb/s
from what I understand, data in and out includes VRChat's networking on top of what your scripts will be sending out (IK, player position, etc.) so the actual amount being sent and received is always going to be more than what your scripts alone are sending
If you haven't seen this, here's some more numbers regarding networking
https://creators.vrchat.com/worlds/udon/networking/network-details/
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
sadly that data is incorrect. and way out of date. even mentioned by tupper on one of their topics on feedback hub
it was last updated Dec 11.... but ok
yea i know. but trust me its not correct.
@frozen igloo you got any updates on this subject
Those numbers are correct, but there are many factors that can affect it. That's more like a peak potential speed
it's a very complicated calculation and it's not as simple as just "greater than threshold = clogged"
i may have asked it before but what again counts towards that max? Per player and their position data? + audio and fbt, face tracking and such?
I don't rely on "sendcustomNetworkedEvent" and my system seems to fair well.
I rely on Serializaton of Variables. Integers, and then those integers call for a custom event locally on everyone client.
hi, have anyone have encountered a bug where the OnTriggerEnter and OnTriggerExit functions are called each frame when a gameobject is held near a trigger by other players?, like in this image, when player A holds the sphere and only gets "enter" and "stay" logs and player B gets "enter", "stay" and "exit" logs.
i am not saying NetworkedEvent.. and also cant use Request Serialization for everything as that wouldn't make sense. some things are notn required to be serialized. like for instance if u catch a player. thats only necessory to know for the one who gets caught. and also any Requests will cause Late joiner event data to happen.
this is kinda half right. One of the most reliable ways to keep people synced is to have a, serialization loop, as I call it. You're right that this doesn't make sense for everything, but for not-often updated things it works great. When a new player joins, they receive the last-networked state of all variables, and also call OnDeserialization. So if you have a setup were: Variables are set > RequestSerialization > OnDeserialization > Use variables to set things, a new player joins this loop at the OnDeserialization part and automatically syncs to everyone else in the world
eh. no just no. you Never ever have a loop that does Request Serialization. its rarely required to update. and if its a set and forget but something late joiners need to know about yes do it. otherwise Never. since again SendCustomEvent is not a network event. and wont trigger onDeserializations and or update something that is not required.
plus your wasting alot of resources requesting serializations if nothing is updated
I don't mean literally a loop....
it's just a loop of things that happen
if you need something to just happen once and not sync for late joiners, that's what SendCustomNetworkEvent is for
well of course don't do things when they aren't required. I feel like that is implied
thats why i mentioned it was not SendCumstonNetworkEvent. it was just a SendCustomEvent.
that i find is skipped sometimes.
but don't just throw away your tools if you're convinced they're completely useless, or you're just making things harder for yourself for no reason
nothing is hard for me lol.
already had a sucessful 2 hour run with 47 people synced perfectly fine. just sometimes a person that joins late had to rejoin due to a SendCustomEvent was not actually send.
which strikes me abit odd. as the theory behind it not happening is well because Udon skipped it
it's more likely there is an unhandled condition where it won't fire
there's thousands of edge cases in everything
very well could be. but i need to get the logs off the people it happens for so.
but then again i am checking for pretty much all conditions that can happen. which in this case is mostly if not all null values.
by their design SendCustomEvent should be used for things that it is ok if it doesn't happen; so if you need it to happen 100% of the time, using SendCustomEvent isn't the way to do it
since the condition of running said SendCustomEvent only happens when the local user has confirmed they got ownership of something
and to be even more so. its actually SendCustomEventDelayedSeconds that does not fire. Or something is going on with OnOwnershipTransferred.
Like pikapetey said, to avoid the scenario where networked custom events can be skipped, you can use OnVarChange that is triggered after serializing synced variables. OnDeserialization works too (although less to my personal liking)
again CustomNetworked events are not in question here.
like I don't mean to sound rude here but if your conclusion of your code not working is "my code is immaculate and perfect. It is surely Udon itself doing something wrong. Clearly events I've told to fire simply aren't happening because Udon just chooses not to fire it." .....you may want to take some second looks
there is nothing rude about that lol. im not saying its perfect or immaculate either.. However it has been stress tested again with 47 people. and it all worked flawlessly. just a few can't remember exact number but it was less then 5 people. That had to rejoin due to a event not firing after they got ownership. which means it likely hit a edgecase or udon skipped it Or it could be due to a persons pc being on the slower end. or a null reference. that said the last one is less likely to have happened..
Pc being on the slower end would affect things if youre using networked events, thats why im bringing it up
Synced variables dont have that problem
That or your logic has some flaw that makes it hard to sync late (like a variable preventing another one from doing its job)
Ya gotta do some testing to isolate whats causing the issue
the thing is. when someone joins. the Master of the world sets a available object owner to that recently joined person. Then once the player that joined gets the ownership they then call a bunch of initializations on the local end + all the late joiner data if any, which is bound per object basis.
so unless its because the master hits a 11 kb/s limit and and it skips the part where it sets the owner.. then that could be why. but then again that also only happend a few times.
mind you. the amount of data that is synced is very low.
atm its like 72 bytes per time someone syncs. but that happens once.
is this a new object for every player? or is everyone transferring and sharing ownership of one object?
Player X Joins. this is detected on the master only. or well runs on master only. then the master sets the ownership of a available object to the newly joined person
everyone owns a single object
once they join
are these objects PlayerObjects?
PlayerObjects don't inherently use persistence
judging by how it sounds, why not use them? Ownership of a PlayerObject is guaranteed per player, no need for the Master to throw ownership around
each individual player can set the values you want, rather than hoping the Master can network it all properly
hmm. you are right but my question is are we able to have colliders on them? etc and other custom things?
yeah they can have anything any other GameObject can have
hmm. yea you know it does sounds like i should change to that.
currently i have 80 custom objects. that i manually set the ownership to. when someone joins and obviously when they leave it goes back to the master. etc
and cleans up that object
yeah you've just got your own self-made PlayerObjects lol.
they should work a lot better for your case
to be fair i was making this before playerObjects became a thing.
that's understandable
do you have these 80 objects already in specific places in the world, or do you place them once a player joins?
How PlayerObjects work is, you just make one object, then VRChat creates a brand new one, in the same place you put the original in the world, for every player
so if they need to be in a different place, you'll need to scoot them to wherever they need to be once someone joins
hmm. i have to check up on it.
because each object atm have a unigue ID that i use to determine which object belongs to who etc.
basicly just 1-80
that shouldn't be necessary anymore, as you can just check ownership of the object. Or just store the PlayerID on it instead
hmm. the thing is PlayerID is a incrementing ID if i remember correctly
its not even unigue?
or rather it is
it is unique for the instance (increments by 1 for every player that joins the world, including if someone leaves and comes back)
so the first player gets ID 0 (or 1? I forget where it starts)
i believe its 1.
I should know I have a debug that displays it in one of my worlds lol
but yeah if ID 1 leaves, when they come back they'll be the next ID, like ID 3 or whatever
ID 1 won't be reused unless there's a completely new instance
so it's very reliable to use something like GetPlayerByID to get the player from just the number
hmm. perhaps. gonna try and convert it and see if this solves the minor issue.
that said. this does create a new object when someone joins. but does it also delete that object when someone leaves?
yes
hmm.
right so i just need to do things abit differently.
cause right now i am not deleting or adding any objects its just all there already and just assigned.
but maybe getting rid of ownership tranfers could reduce the overall strain.
- also get rid of any relience on it
hm if you want everything to kinda look the same, you could turn your current objects into just visual placeholders, that you then disable once you replace it with a real PlayerObject?
dunno
A good starter test for PlayerObjects is to just make a VRCPickup
then you can kinda see how they behave per player
eh i mean if they behave the same way as im thinking it shouldn't be an issue
its just alot of ehm. things i have to redo lol
Yo, does anyone know which encoding udon uses for string networking serialization?
assuming unity's default (utf-16 defaulting to 2 bytes per any character, +2 bytes per special character, +4 bytes per string)?
Has anyone encountered such a problem?
yep
you need to regenerate Network IDs
I should really keep the screenshot for it handy....
this article goes over it
https://creators.vrchat.com/worlds/udon/networking/network-id-utility/
A network ID is the identifier that is used to determine which object is which when it comes to networking. In most cases, you don’t need to worry about this, but it can come up when working with cross-platform worlds where players are technically loading two different versions of your world.
Open the Network ID Import and Export Utility, then in this window select Regenerate Scene IDs
@finite folio
Thank you, I'll try in a while.
Is there a way to sync animators? I have a couple npcs on a navmesh and I have their positions and rotations tracking, just need their walk cycles to track too
its utc sync for looped animators, wont work for navmesh
oh
for npc i usually set animator state in code (ie if agent isstopped, set int for animator to 0 which causes anystate transition to stop animation) so that should be easy to sync (set not animator but synced var, for everyone if synced variable changed - set animator variable), but never tried.
Thanks for the help! It had occurred to me that variable syncing would be the solution, I had just thought there was some kind of unique answer for the animator
What causes a "Serialization failed for script" warning? Is it an issue of the data being too large to send, or something else?
I think so, can also happen if you try to sync an uninitialized array
it can fail if its to large. if its of a unsupported type, etc. but being to large is doubtful as that is 280.000 bytes or 280 kb. also the variable your trying to sync does it have the UdonSync on it?. also if my memory serves right we cannot udonsync arrays
I swear I saw or was able to sync arrays before but you’re right it’s not showing up on the documentation 🤔
It’s a string array that’s currently really large, ima rewrite my code to use a specially formatted string instead and see if it works then 👌
Try not to use strings as synced. They are very large. Each character is 2 bytes typically. And if I remember correct. Udon limits strings that are synced to 64 chars I think it was?
You can sync arrays of primitive types. However strings have a gotcha, as they can be null, and having a null string in an array breaks the serializer (because programming is hard).
Oh so I just initialize it with empty strings 🤔
I’ll give that a shot too!
here's everything you can sync, which includes arrays of these types
https://creators.vrchat.com/worlds/udon/networking/#requestserialization
Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
dunno why this info is under the RequestSerialization info instead of like, in the UdonSync page, but this is the general Networking doc
It’s also under https://udonsharp.docs.vrchat.com/vrchat-api#synced-variables
I think that was the page I was originally looking for
But oddly this one doesn't mention arrays also being allowed, I guess it's implied here
I am having a problem with the SendCustomNetworkEvent, Is this not how you use it? The PlayAnimation gets called but not the PlayNextSegment.
so you're seeing the "PlayAnimation Called..." message, but not the "Resuming animation..." debug message?
so that would mean it is probably calling PlayNextSegment, but you have an if statement that stops the function if it evaluates to true
You could double-check if the function is running by putting a debug log before this if statement
The if statement is checking for null and i know they arent null, I will test the debug before it to be safe.
Still not working can upload and do a live test too. Here is what i see in unity.
what sync mode is your script set to?
This is at the top... Is this not good? "[UdonBehaviourSyncMode(BehaviourSyncMode.None)]"
Guessing that could be the problem. smh
very not good! setting the mode to None disables any networking on the script. So SendCustomNetworkEvent will do nothing
if you don't want this to be networked, then just SendCustomEvent
Well that was stupid. Thank you for the help anyways.
I just removed that line at the top.
that should work
you can also set it to Continous or Manual there too, that way you don't have to worry about setting it in the Inspector
I like setting them in the inspector for the most part lol.
[UdonBehaviourSyncMode(BehaviourSyncMode.NoVariableSync)]
always include it (even .None if it is none) and in this case there are no variables to sync but you want the network event to work
I'm back with more confusion with arrays. I'm using a FieldChangeCallback on a synced array and notice that late-joiners receive the data as expected and all is well, however all remote players in the instance at the time don't seem to be running a FieldChangeCallback off that array when it's updated by another player, and they have to relog in order to re-load the array
Any idea why that would be happening? 🤔
FieldChangeCallback probably isn't what you expect it to be on arrays. It operates off of a very simple premise, that it will fire when the variable itself changes. It does not look for deep changes of the things within that variable (which would be kinda impossible depending on what you mean). What that means for arrays is that it only fires when you construct a new array and assign it to that variable.
That said, there's an additional complexity with synced arrays which is that for performance reasons, it does not recreate the entire array when you receive synced data. This means that remote players have no idea what assignments are happening on the owner's side, all they know is the final resulting data. As a result, remote users only get the FieldChangeCallback when the length of the array changes, and will not receive anything when the owner just updates a single variable or reassigns the array to the same length.
Ultimately, FieldChangeCallback is just not very useful on arrays and there isn't a good path forward for them to ever be useful in the way you'd expect, so the best course forward for you is to simply use OnDeserialization instead of FieldChangeCallback
fun fact: arrays used to not have the optimization of keeping the same array when you receive synced data, which technically made them work for FieldChangeCallback, but not because it was detecting changes, rather because it was erroneously firing it every time you received any serialization, even if there was no change. Some people hooked into that and assumed that it was actually detecting changes which was not the case, and also it was terrible for performance to repeatedly recreate arrays like that. So if anybody ever tells you they used to work, they're talking about that. But we're definitely not going back to that way
lmao that's good to know, I completely understand 👍
How exactly do you use OnDeserialization to detect changes to an array? I have yet to write any code directly there, I just know that's one option of processing network changes
Creating arrays isn't bad, also udon sync creates one regardless so the "optimization" added an array copy and didn't save any allocations
That change in particular was dumb because it broke a bunch of stuff under the premise of a handful of array allocations being show stoppingly bad when that couldn't be further from the truth
Regardless of the exact cost, it's not accurate to call FieldChangeCallback for that scenario
People were using it and assuming that it only fired on change when it was actually firing on all serialization
I mean now sure because vrc broke that contract
Now it fires when the array size changes
it was not a contract, it was behavior that was never defined in the first place
So it's more weird honestly
No behavior in udon has ever been defined, that's why all the high end creators leave
sure it might be more weird but if it's gonna be weird, I'd prefer the weird that people don't rely on
Vrc aggressively is against providing a reliable system
the only way to make it not weird is to implement a deep change check which has other design problems and paints us into a corner with implementing future features
Sure, failing that the less weird interaction is to just do a bit of redundant work when the array doesn't change
sure, it's entirely valid to have that opinion but that does not mean it's the objectively correct solution to this problem
the real problem is that there is no objectively correct solution
There's no objective correct solution here, it's subjective. The problem was the system was working better and vrc changed it after the fact to make it worse
that's an opinion though
I mean this discussion was started by someone assuming it works the way it did
If it's going to fire on every serialization anyway, then my opinion is that it's better to point people towards OnDeserialization so they can think about implementing their own context-specific change check, which is entirely doable on their end
which, speaking of: If the cost associated with this operation is cheap, then just do it on all serializations, it's fine. If the cost is expensive, then it might be worth writing your own change check. You can do that by just keeping a local copy of the array and comparing it in OnDeserialization. If the array is large, then that change check might get expensive enough in it's own right to make it not worth it, but you can pull that back by making a more optimized change check by doing something more advanced with hashes and/or bitshifting
I'm still ironing out bugs toward the end but I've mostly figured out the easiest solution to this atm is just updating a boolean when I make changes to the array and using a FieldChangeCallback on the bool to call the code that needs the array
Not the best resolution but if it works, I'll be happy with that
eh, I'd be resistant to that kinda thing because it goes against model-view architecture. If you want to solve it by the owner communicating a change, I'd probably have them increment a byte
Hashes and bitshifting? Sounds interesting
lack of documentation changes. is why. there is alot of information we all have to get from other people that found out the hard way. Vrchat is not realiable when it comes to informing people of important changes.
yeah there's a surprising lack of documentation on anything udon
JavaScript introduced the concept of a "Proxy" which is a really neat language/runtime-level feature to solve exactly these kinds of reactivity issues.
It basically allows you to create wrapper objects around other objects that intercept things like property getters. They are restricted to not change the interface and are therefore equivalent to the external consumer code, but allow you to implement custom logic (like implementing observers/signals).
So this basically lets you solve deep reactivity for objects created even by external code.
It is so incredibly giga cursed, but its also so fucking cool...unfortunately C# will probably never get this 😔
you do know that this is a default thing in C#? by using observer patterns, etc or watch patterns.
you can essentially protect everything in C# and not expose anything to external.
also c# has the Proxy pattern.
No this is very much not possible in C#. JavaScript works fundamentally different than C#.
Its not a pattern, its a built-in language level feature.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy if you want to read into it
The difference is that this can make existing code reactive, by building your proxy around it.
which sounds like a trap for performance. hence why its not there.
everything in c# is reactive through anyway..
No? If some object changes its own property there is no way (by default) for another object to react to that change without polling
You would have to build the observer pattern into the object you want to observe
class Foo {
int bar;
}
Well I guess in C# field is the more accurate semantic
Properties are technically just syntax sugar, as they generate functions that manipulate a field
But the argument holds for both
yea? and if u change that anything else that uses it knows it changed.
unless u mean u want it to notify it changes>?
i know exactly what it means.
i work with c#, javascript, typescript alot.
vue to.
and such
anyhow
why would u want that in c# anyway? if you need it you use patterns
In C# you have to keep a record of observers, and tell every observer that you have changed.
With Proxies in JS, you can reverse that.
Well not "reversing", the proxy basically hooks into the object and does the same. But you can "add" reactivity to any object.
Thats the crux
well the reactivity in pretty much all front end is just in the back most likely a observer pattern of some sort. that holds onto what changed and informs you back.
Well yeah, but due to proxies you can add reactivity to objects produced from third-party code. Thats the cool part.
What "OnFieldChangeCallback" tries to do is provide shallow-reactivity support, so you can react to an external (network) change of a variable.
But deep-reactivity would be ideal (at least for a lot of things)
but then again that only also works because front end is rarely real time. its at best a few seconds or so.
problem is cost ngl. and memory cost most likely.
It would at the very least solve the problem that was mentioned where the callback only gets invoked, when the field changes reference, as opposed to when anything changes within the field.
its prob something they did to reduce the networking cost.
Deep-Reactivity does not incur any extra performance cost when implemented correctly. It is usually even better since you dont need to recalculate a lot of things.
say you put X = 1. and u then change to X = 2. a change happen. but if u do X = 1. to save on networking it does not notify you. most likely cause it doesnt fire.
well. it would have to keep it in memory. and depending on how big that change it if you have a couple of 1000s of them you can easily start running into overheads of a couple hundred mb i imagine.
obviously depended on what is being changed but im sure there is a good reason for it.
seeing in games we often change 10000s of variables every second.
Well yeah thats mostly why game engines dont do this. Its primarily only useful for UI/Visualization.
Games are very interactive, but in the end a Computer + I/O (when used for gaming) is nothing but a very complex interactive lamp.
You can derive the state of your screen (the final rendered frame) entirely from the state of the game. Its just that a lot of things happen all the time and instead of reacting to everything, its much easier to just recalculate everything every frame.
Since its highly unlikely in any game that nothing (relevant for rendering the frame) has changed since the last frame.
But for UI that is not true
thats where the power of subscribe comes in 😄 for UI. often when i create UX outside of Vrc in this case for games. i bind the UI to what i want it to react to.
any changes etc.
like new item in inventory
deletes an item
and so on
visually connect things but not expose any logic
since i always keep UI/UX completely seperate to game logic.
Is it difficult to have a world where you can spawn NPCs and sync them between players? I'd like to do something like that but it seems rather VRC-specific and I'm not sure if the documentation covers every problem that I might run into
It is fairly difficult, but not impossible
Udon already hates me, Unity crashed as I made a new script, and then the script became null.
I can fix it though. And I'm a madman who uses notepad as his IDE of choice
I've found a video on VRCObjectPool but it uses the udon graph and not raw code. I don't care how many manuals I have to read, only that they exist
"VRC.SDK3.Components.VRCObjectPool' is not supported by Udon sync" so I'm guessing the sync is built in?
I'm a vim user, so...... yeah
Got object spawning working. But sometimes I need to despawn objects, and the pool doesn't know which objects to despawn (it uses TrySpawn, so it can't tell the spawned objects apart). The objects could tell the object pool that they should be despawned, but I'm really not sure which object should be in charge of what, or what direction to communicate in, and I know from experiences that circular references lead to problems. This is probably common knowledge but where would I read about it?
I got it.. The players sword will get the object it's hitting and check if its HP is below 0. This is going to be an interesting project
The Object pool component is broken. It does not disable the objects in the scene when it starts
Oh my god, it works if and only if the scene has a scene descriptor in it
Edit: 40 minutes later my program works, I can now kill slimes
how tf you ended up with no descriptor in the first place, theres fing template loaded on new project
I don't use templates, I make everything manually. I will look into them in the future though
uh, you download vcc, you create 2022 world, you open the project - theres already a descriptor. if youre doing any of it some other way, its shooting yourself in the leg, feel free to do so, but suboptimal.
I'm not using VCC at the moment, but perhaps I should, it sounds neat. I'm just worried it will mess with my current Unity installations (I have 5 already) since I installed these before Unity Hub was a thing (which I still don't have)
it's easy enough to drag the world prefab into the scene though
hows hub is not a thing when its 2018, like do stuff the proper way ie through vcc and stuff to get relatable support
For me, the world was a component I had to add, it was not a prefab. There may be a pre-configured prefab for it in the SDK example folder though?
yep, there is
not really no. but it would be limited to how many you can have. as each npc would require a synced state of them being active or not. so basicly a bool. and they would all be owned by the master of the world.
you dont want to have the weapon check anything. apart from telling it hit something and did damage. the object it hits should be the one that decides if its dead / alive.
A bool takes very little space. I think syncing location is more expensive
I will change to make the enemy know that it's hit.
I think I can do 20 enemies for now, it shouldn't be worse than having 20 playing cards in a game world
that doesn't matter in udon sadly. even if a bool is only 1 byte. the overhead alone seems to be 10 bytes. and we have limited request serializations per second. etc. obviously 20 objects isnt alot. and if thats the only thing that is fine.
The update rate of synced objects can be low (say, 0.1s) and I could possibly use bit manipulation or mathematical tricks to save data, at least for objects which have a deterministic location
Bit manipulation or any tricks wont save on the serialized data. the overhead can't really be mitigated. and at some point the update wont even happen.
also location? u dont have to sync or do anything with that.
All important objects need to be in the same location for all players (for multiplayer functionality). If they were NPCs with specific routes, I could calculate their position and sync just the time simulated, but I plan to have NPCs which follow players around. But you probably just meant that the sync is automatic, rather than not required?
Could someone help me out this is driving me crazy, So Ive made a world and it has quite a few objects which use all manner of different Udon scripts to make them do stuff, The world works great at high FPS when playing in it solo.
The problem starts when someone else joins the session, randomly the world will begin to lag and stutter... HARD going from a stable 150fps to blipping to 80 every second back to 150 then slowly devolving to worse and worse performance the longer the session is active, eventually everyone in the session will lag soo hard they cant even use their menus anymore and have to Alt F4.
I used the in game debugger tool to see if there was anything going on and I noticed that a few items and assets that were using Udon scripts were sending update data once every second, this made sense because I have a few scripts (See third image for example) which I made to run on a tick system (as to avoid running code once every frame)
The image shows an udon graph which uses a trigger enter/exit to tell if a lockable room has any players in it, if all players leave the room while its locked, it automatically unlocks so new people can walk in.
This all works great and greatly improved performance of my world when just testing it on my own but the moment someone joins its less than 2 minutes on average and the world becomes a black hole.
What on earth is going on? Surely you cant tell me that running these simple scripts once per second (Theres like 10 of these scripts running at once on average) is causing everyones CPU to die?
Furthermore I notice that even objects that are disabled in the Unity runtime that have scripts like these, ARE STILL RUNNING BRUUUH!!!! So you cant optimise your worlds code by disabling objects that don't need to do anything?
I think what's happening is that when your persecond event does sendcustomnetworkevent to run tickcount, it's creating a new loop and every second, it adds more loops. When there's only a single player, it's a stable loop because one event triggers one loop, but when there's two players one event causes two loops and two events cause 4 loops and it continues exponentially from there. It devolves into worse and worse performance because it's adding more and more and more events every second, probably into the millions
yes, that makes sense
Im not sure how I could stop that... maybe a host controlled udon that acts as a tick master? But I dont think theres anything for that?
Is this entire structure just to unlock a door? You should do that with synced variables, not network events
No, the unlock door feature is a seperate udon, the one illustrated just detects that theres no players in a locked room and tells the other udon to forcefully unlock
after 3 seconds or however I put in
Ok then you should have a local, internal event loop that checks time and not use a network event for that. Once you actually detect that it's time to do something, then you do networking. I would probably have everyone run the once per second check, but then only the owner of the door actually applies the change to unlock the door. But that's assuming that the door Lock state is a synced variable, which it should be. If it's something else like using network events to sync a door Lock state, A) I wouldn't recommend that and B) that would have different requirements of who needs to run the code to change it's state
Yeah, makes sense
Lets see...
There were a few other things that were causing it too from what I could see, I just wonder if theres a way one can make a udon script that only the host calculates that could send networked global events
I mean yeah that's what an isowner check is good for
Only one person is the owner, they determine state
Oh yes good idea, Il use that I think
Network events are ideal for bursts, they are not good for syncing state. State is better managed by synced variables and ondeserializstion, please give it a try you'll have a better time and it will make a lot more sense
Will do, thanks
literally setting this up today, thanks for the timely advice 🙂
To elaborate, for example, a door (assuming this door swings open and closed, teleport doors would be different)
There's two key bits of information relevant to the door:
- is the door locked?
- is the door open?
For every synced object you work with, try to condense it down into these key pieces of info.
Then, to implement a door with synced variables:
- create a synced bool representing the open state
- create a synced bool representing the locked state
Door state doesn't change too often but when it does, you want it to be quick and only once, so manual sync is good for that.
- Make sure you set the udonbehaviour to manual sync. (Continuous sync is better for things that change often and at a consistent pace, so there are use cases but manual is usually more common)
When you want to change the state of the door, all you need to do is have one (1) player execute this series of steps:
- take ownership (only the owner can change synced variables, unlike network events)
- set the variable
- run the requestserialization function (this tells the manual sync to do it's thing)
- apply the new state of the variable into a visual representation in the world (like setting an animation bool so that the door plays an open/close animation, or toggling objects, or doing whatever in the scene so that the player can see the door is now it's new state)
Then, on the remote users side, they will receive the ondeserializstion event. This event indicates that you have received synced data from the owner. All you have to do now is run the exact same "apply data to world" code after getting ondeserializstion, and now everyone will see the same thing.
Kinda curious what you think the ideal setup would be for a teleport door
I've got some stuff working already but it may not be ideal. It also has an animator call because the lock visibly changes.
https://creators.vrchat.com/worlds/udon/networking/network-details/
Hey, I feel like I'm misreading something here, or something's a tad bit wonky in the docs.
Since I'll likely have to deal with the limits later on a project I'm working at, I'd love to know what they are for manual sync.
Are the statements on the website correct?
[Red]
These seem to contradict one another, unless a sync has changed to take more than a second since last time I experimented, and unless a single sync can somehow be performed by multiple scripts working together to create a mega-sync...? I'll assume the worst of those values for now, but clarification would be amazing.
[Blue]
This seems very odd. I require a ton of high frequency, high importance (absolutely no loss allowed once connected) syncs, so... should I then queue them, to not request syncs quite as frequently?
[Green]
In an old iteration of my code (~3 years ago?), one I deem fairly dirty, I used multiple manually synced objects for one main-script to distribute sync requests across multiple scripts. Back then I did that because I didn't know better - but seeing this, is that actually the correct way to handle syncs for my requirements...?
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
red - 11kb on average (actually more like 8), 280k max single behaviour can sync at a time ie its synced variables combined should never come close, 64k 🤷♂️ probly just old data
also afaik having multiple scripts to serialize the same amount of data will increase overhead significantly compared to one, multiple variables in one script as well but not by much, so splitting should be bad (ofc if you need all of that data to be updated often, splitting data that updates every few seconds and one that updates rarely is good since you cannot do partial sync in one behaviour). afaik rate limit happens only when, well, its rate limited and networking is in suffering state.
hmm. At least re-writing makes sense then. nice.
guess I'll combine it all then to reduce calls; It's all the same type of event.
I can probably get my events down to 6byte per, Update it as a serialized string plus metadata and overhead 5 times a second (accepting a 0.2s delay), and boot out information older than a second...
that should get me to roughly 250 ordered syncs a second if i take the 8KB as a guideline.
Will try to set up a prototype and try its limits with a few people globally on the weekend to see if that gets the job done.
It's less than I hoped, but definitely manageable, thanks :)
people with a ping above 1s might experience issues, but that's a sacrifice I'm willing to make
whats the point of trying for 250 syncs with 80-150ms ping and 400ms audio delay tho.
it's not that much about the ping, that's why i can accept the 0.2s delay without issues; it's about everything safely arriving at a consistent rate without slowing down on one client's end.
I admit it's a niche use case, but at least two of my currently planned projects need this as a base line.
or, well, my two planned projects. No "at least". No point in starting more atm :v
you should never do a string. its heavy AF. every character in a string is typically 2 bytes. meaning just a 12 character string results in 24 bytes. + udons overhead which i found to be roughly 10-12 bytes per synced variable.
also you cant even take the 8kb as realiable either. everything on your avatar that is synced, animations, other things like osc and such all plays into it
ontop of that audio from you to
typically i find my self with fbt, face tracking and talking to take roughly 3-4 kb alone
yes yes, but i have an event consisting of a few ints, a pseudo half-precision float and some metadata that gets formatted to a 3 character string, which that gets inserted into a collection of events (a larger string).
It's just a more optimized way of networking a continuous data stream with nigh guaranteed arrival.
I'll probably only hit ~80 events per second, the 250 are just what I'll use for the absolute max I should expect to handle.
I'm not actually syncing some long language-based texts several times a second 
if i have 100 events a second continuously, it'd be a ~620byte string, serialized 5 times a second, for a little over 3kb per second.
Eh. No. Since the overhead is 12 bytes. On top of the string.
it's only one string though, not many. the overhead is once per string, not per event
i update the string locally with events, yeet out old ones, and sync that string (containing many events) 5 times a sec. maybe even less if testing makes it seem like a good decision.
I'm just abusing string as a byte array, so that I don't have to worry that much about manual resizing.
will also make it easier to later refactor to use enumerables instead, once soba finally enters public testing
Eh. I don't know what your trying to sync that requires 620 bytes worth of a string.
welp, some 100 6B events plus some overhead xd
What events. And also it's not really possible to have A 3 character string that tells what needs to be synced. Are you going to manually write every int. Float combination in hopes to send less data?
i compress my ints into bytes since i only need a specific range, take 2 bytes for a half, and just handle the bytes of the string separately.
on OnDeserialize, the client deserializes the message back into my events when needed, plus minus some optimization to not read the entire byte array every update.
as an exanple, you can turn every (default precision .net-)float into a byte array of length 4.
you can turn a byte array of length 4 into a string.
you sync that string
receiver knows: "hey, that string is the float i ordered with prime shipping!"
-client turns it back into a byte array, and parses the float.
That would result in minimum of 2 characters per byte. In the array.
...huh?
not quite; it's 2 bytes per character. unless it's interpreted as a special character from extended set, then 4 bytes per character. but the characters themselves aren't even anything I'm worried about -
the fun part about this is that the string doesn't even need to be valid.
this is directly from microsoft them self. byte[ ] arrayOne = {
0, 1, 2, 4, 8, 16, 32, 64, 128, 255 }; turns into 00-01-02-04-08-10-20-40-80-FF
assuming u use ToString. for the BitConverter.
hm... i don't directly see why this would be the case, but it's certainly possible I'm completely off.
I'll make an attempt tomorrow probably, do you want to be pinged with results?
I'll head to sleep for now, gotta wake up for work in 5 
there are different ways to encode strings in a byte array. Some take a single byte per character, but are limited to a very strict set of characters. Some encodings take a variable number of bytes per character, so most common characters are a single byte but more complex characters will use multiple. And some encodings always take multiple bytes per character
the details don't matter too much unless you're writing your own implementation but just be sure you know what encoding you're using and that it's consistent between serialize/deserialize
Well most if not all turns it into a hex. Again if you use to string.
On a byte array atleast
right, that's the raw representation of data. But if the data is supposed to represent a particular encoding of a string, then that won't be correct
One example of how all this works together is that ASCII and UTF8 both have the same first 128 characters, and they only diverge after that. So if you never bother testing unusual characters, you won't see issues caused by mixing up your encoding type
furthermore, it's easy to assume 1 character = 1 byte if you only test ABC123. Because in nearly all encodings, those fit within the first 128 bytes
Mhm
Not to mention the cost to encode and decode. If you have to do that 5 times a second. Per receive and send. And you then have 10 people that's already 100 times a second.
I can't imagine converting is cheap. Either.
In udon that is
well when you send a string, all this stuff is already happening on the backend. So yeah, doing it in udon too is redundant and will come with an additional overhead. That overhead comes from the cost of running udon itself, and also how udon doesn't have the ability to do some optimizations on running that process like better memory management of this process.
But doing it in udon also allows you to do things that the native system doesn't do, like extra flexibility and runtime configurable variables. If you don't actually need that additional flexibility, then it would be a bit silly to go through this effort, but I doubt that is usually the case unless someone is very confused and new.
I can imagine. That
That does make me wonder if it's possible to reduce 2 longs, a byte and bool. Further down. And thus reducing udon overhead cost
if you don't need the full 2^64 for a value just bitpack the smaller types into the same long
// Pack before serialization
long longValue = 123456789012345;
byte byteValue = 204;
bool boolValue = true;
long packedValue = ((longValue & 0x007FFFFFFFFFFFFF) << 9) | ((long)byteValue << 1) | (boolValue ? 1L : 0L);
// Unpack on deserialization
long unpackedLongValue = (packedValue >> 9) & 0x007FFFFFFFFFFFFF;
byte unpackedByteValue = (byte)((packedValue >> 1) & 0xFF);
bool unpackedBoolValue = (packedValue & 1) == 1;
soooo kinda. Here's the thing:
- If the goal of writing your own serializer is to make it more efficient on byte size, then you're competing with the native udon serializers. There are many ways to serialize, so let's not immediately assume this is impossible to do. But if you are going to, then here are the challenges to be aware of:
- Native udon serialization is wasteful, which means on the surface this is actually not a hard thing to do! It very commonly throws in extra empty bytes all over the place, so nearly any custom serializer you write is going to be more efficient on byte space.
- But why does it add in those extra empty bytes? It's two-fold: the first reason is that doing so allows it to account for byte alignment in memory. This allows it to run CPU operations quicker because in most cases, the bottleneck of a CPU running code is on moving memory around, not doing the actual thing. As a result, accounting for byte alignment can be extremely effective. Due to this, along with the overhead of udon, it's basically impossible for you to compete on raw speed, because the frameworks picked for udon serialization are specifically designed to optimize for speed.
- If that was the full story, then it would be very easy to beat udon serialization on bytecount, because it is explicitly wasteful on bytecount in order to target speed instead.
- However, that's not the full story. If all those empty bytes were sent raw, it would be quite wasteful. But that's not what it does - before it's sent, it also runs through a compression routine. And compression is extremely easy on datasets that include a lot of empty values. The real kicker is that the final bytecount of this two-stage process is almost always better than a single-stage serializer that spends more time to pack data more efficiently, reducing empty space.
- Furthermore, the numbers you see (such as serialization bytecount) are typically pre-compression, and not all data is treated equal. For example a 100kb array of empty bytes will compress down to nearly nothing, while the same array filled with random values will be impossible to compress at all. Likewise, the output of a serializer which ultra-efficiently packs the data into as tight of a space as possible, cannot be compressed very well.
- The end result is that compressors are very good at squeezing out the real data, and the actual arrangement of it doesn't really matter too much. If you attempt to compete with the native serializer, your pre-compression size may be better, but the post-compression size will ultimately end up being roughly the same.
- This is why it's far more important to think about the source of the data instead, minimize your state down to the smallest amount possible and reducing unnecessary serializations in the first place. This is why separating your big data from your fast data is so important.
is synced data compressed? I've never seen that mentioned beside the actual on-server storage of persistent data
that'd be good to document and show to world authors in some way
From what I understand, yes. One of my goals this year is to build a better understanding of our networking stack (and document them). Some of those details are still fuzzy and need more investigation (such as how suffering works, and how it interacts with compression).
Hmm. nvm it was actually two uInt and 2 bytes i am udonsyncing. total cost 28 bytes. + a 12 byte one for a SendCustomNetworkEvent in another script seperated.
would it be correct to assume that since Vrchat uses a serverless architure that when we create an instance, that vrchat acts a relay between the users in the world? @frozen igloo
I mean vrchat is not serverless?
There's a relay server that routes, processes, and caches traffic between users in the instance
oh well someone once said that in here. anyhow. how come an byte array with length of 16 is 80 bytes ? @frozen igloo any knowledge on that. when it comes to the udonsycned
byte alignment, as described above
to the best of my knowledge
hmm. so if its saying 80 bytes both in the bytecount and Debug Menu 6. its likely not actually 80? but more likely 16-18 bytes + the overhead of udon?
do we have a solid way to check what the compressed value is or is that just something we have to hope is done?
hmm gotcha
Hm. Didn't get much done today since hordes of hel released and I got carried away, but I did realize I got ahead of myself.
For some reason I remember it not being possible to sync arrays, but apparently it is perfectly fine for syncing. (was there an update or did i just miss this several years ago on the old iteration?)
So just doing that for my QuickEvent structure thingy could be a lot simpler.
Also for simplicity I'm dropping the half and just testing with a float for now, increasing my event back up to 8bytes, until I run into issues. I can still decrease it later if needed.
I've converted the rest of my code to use bytes instead of the ints anyways, so only one value inside the QuickEvent needs to be parsed to send a bunch of them over as a bytearray.
Now I'll just have to test what runs better outside the networking at some point; syncing an array of varying size (Let's say <~2040 bytes) and handling up to ~257 array resizes per second (lord have mercy, grant us queues), or syncing a string of varying size parsed from that (<~255 characters) and handling the same amount of resizes; though through more easy means (+=).
Guess if both are contiguous, it probably won't make much of a difference, but I'll still have to try.
For the networking itself, I'm setting up a test scene with a slider and some feedback on when cramming occurs on anyone's end, so I can gather estimated limits with a few connected clients...
yep, you can sync an array of any of the already supported syncable types; just make sure to initialize the array before syncing or serialization will fail
Note that if you are trying to implement a packet type system that udon does not guarantee every sync finishes. Only that the last stable state is what everyone in the world will get
oh and also, I believe queues are supported in the Udonsharp beta
ye I know and I love it, but since it's for an asset I'm planning on having a sellable version of, that would mean I'd be forcing the clients to switch their entire world to the beta. Which is bad manners.
betas will likely become releases though
Its not a vrc beta
We might not even get that beta though, depending on how soon they plan to introduce soba
oh true
I have a feeling that while u# wil stay supported, its active development will come to a halt when soba does release.
yeah it sounds like Udon and by extension U# will just be kinda considered "legacy" and we'd all be meant to move on to Soba
If Soba's promises are fulfilled, I don't think I'll be too upset though
same
at first glance, I didn't really understand anything they were saying about soba
which leads me to believe it will be more complex in some way
so I'm expecting it to be for "advanced programming" and U# will linger around for people who want easier concepts
i'm really not expecting it to be difficult to port existing u# to soba either. wishing them best of luck with their current development, especially after that harsh initial community backlash
uh. depending on the size of that array you will be shocked to fine the insane overhead. a byte array of length 16 cost 80 bytes..
considering its completely different im not sure there is any porting.
also 257 resize of array a second? thats way to much and it will cripple udon guranteed.
manual porting will be pretty simple.
your code's logic and principles won't change.
then again i might be exaggerating the simplicity because rn at work I'm tasked with rewriting a c# unity app in native swift for vision, which is more than a little pain in the rear
oh im sure you are lol. depending on what we are able to do with Soba. the logic will most likely change completely. since majority of things done in udon is kinda forced to be done poorly. you have to do things you normally don't do in c# etc
it'll certainly open up doors for improvement, but i don't expect it to make previous awkward workarounds impossible.
More of an opportunity than a hurdle.
well. im just hoping for structs, non udonbehaviour classes, scriptableObjects. all of these would promote a much more scalable code in general.
oh and Parameters for events.
first two will most definitely be part of it, no doubts from my side
and im hoping that Traversing Udon behaviours or aka Soba at that point is cheaper. since thats also a pretty bad thing atm.
cant really do component pattern. or command pattern that easily
Is there a weird condition around when a Vector3 is actually synced, or is it just any changes to them?
I'm getting some wonky behavior where the first time I spawn in a pooled object it's fine, but after killing and trying to spawn in a second time its spawn position doesn't appear to be correctly updated for remote players
I'm trying to figure out why this is, it's really weird to see
what type of object are you dealing with?
I actually did just fix the bug by zeroing the Vector3 out when I reset the object
I guess updating a Vector3 from not zero to another value isn't serialized properly, sort of like arrays requiring specific changes to serialize properly too
so i put this little animation trigger randomizer together and am wondering what the best way so sync this would be.
kinda struggling to wrap my head around what to do about getting this to only run on the masters client and also sync the random chance to play value within the for loop (don't believe this is possible however) so everyone sees the same animators animating.
Whenever I'm working with randomization and need to network it, here's a technique that works pretty well:
- The Owner (not master) generates a random value.
- Store this value as a synced variable.
- Owner then uses this value to set what is needed, then calls RequestSerialization. Or, use the variable's OnValueChanged event.
- When variable syncs, remote players then use it to set what is needed.
As you've probably realized, your current script will have everyone run Random.Range, so everyone will get a different value.
I usually have 2 functions, a Setter function (where the random value is generated), and a Updater function, which actually uses the value. I make a check to ensure only the Owner can run the Setter, but OnDeserialization I run the Updater function so remote players get the change too.
I recently solved this issue by having the owner generate & sync a random seed on each item when it spawns, and using a field change callback on the seed that starts by Random.InitState(value) then sets all the other randomized values, that way all players will get the same random values for that object
@high elm @strange token I solved this issue by setting up a various modules that handle network syncing and buffering for people with slow internets.
Here is an example of a function where the Owner of the NPC chooses a random point in space, then sets that info up for everyone to receive.
these work for a single randomized value but i need to randomize the triggering of animators in an array, i assume i would have to somehow also store the random values in an array and sync that?
and also if the owner leaves how would i make sure the script hands off control to the next owner correctly?
you're right on the array part, and if the owner leaves the values will still be synced to the instance, so whoever gets passed ownership will be taking care of it then
If you set the values as part of OnDeserialization, late joiners will also receive the correct value
not sure if i've gone about this right.
I think so... it's a little hard to read
hmm for SendCustomEventDelayedSeconds, you delay it by a random value but don't store it
and this would be happening for the owner, so the owner will randomly have it delayed, but everyone else will start playing once they get OnDeserialization
which i believe is sent by the same loop as the delay so that value doesn't need to be stored because only the owner needs it to send the OnDeserialization event
ok makes sense
i also have a send custom event at the end of the loop because it dosnt appear that OnDeserialization gets sent locally which makes sense
that's right, you'll always need the owner to run it for themselves
this dosnt seem to be working though
i have 3 test clients running and there all seeing different things
am i getting the owner correctly or do i some how need to set an owner?
its acting as if every player is the owner
somehow ownerlockout is getting set to true for non owners
i think
but at the same time not because only the owner is sending OnDeserialization
im so confused
ok so somehow this always passes
could this be just a local test issue?
ah that's because OnPlayerJoined runs for everyone
so when the owner joins, this evaluates to true
even if you aren't the owner
oh god
so what you're currently doing is "the player that just joined, are they the owner? yes? then I'll go through and randomize this thing!"
instead, you need to check if the local player is the owner
like so?
yep
you have to be the owner to write to synced variables and call Deserialization, so weird stuff will happen if you aren't the owner and try to call it
tysm i think this works now
Correction: regular synced data is not compressed, only persistent data on the server side after it has arrived. So that does change some of my conclusions, and means that packing data tightly or doing your own compression with udon does have some benefit
What is the first playerID assigned to the first player to join an instance? Is it 0 or 1?
I thought it would be 0 but it's looking like it's 1 so I want to double check if anyone specifically knows 🤔
0 is reserved for ✨ something else ✨ so players only ever start at 1
Thanks for confirming! I can unshittify some code I wrote today to make rewriting it easier :D
playerID=0 is lurking in all of our VR instances. Watching, waiting.
Sometimes a glitchy mirror will show it, for just a moment
on the topic of playerIDs... turns out InstanceOwner checks the player account, while Master checks the id.
So you can have stuff like "my id: 2. InstanceOwner id: 1. Am I the owner: true." which imo is strange and can cause issues for offline testing with multiple clients. Should simulate different account too :/
master != InstanceOwner necessorily.
can be two different users
0 is the photon server itself iirc?
There's also some range reserved for players too (world network IDs)
correct, master is just whoever's session in the current instance has been going on for the longest time.
But the fact that owner instead checks account means that in offline testing, every player, even with different id, is the owner.
So I know that Requesting ownership of an object from 2 clients is a no go, as one of them will win and the other will get desync'd, is there a way to ensure only one of the clients always wins?
Just trying to see if I can avoid having to make it so my board game has a second during a phase where people can't change any of the pieces/cards when the Network master comes in to do stuff during the phase change
I'd probably separate things into different objects. Keep important phase change directorial things in one object that only one person owns and manages, then everybody else can fight for and manipulate the other object that has more ephemeral state
either that, or just have the current owner do the phase change, whoever that might be
So GameState is its own class, I'm specifically talking about "Cards" which players can pick up and move
During different GameState driven "Phases" the cards are ripped out of players hands and modified
My issue is if the player places a card down, it updates the data in that object, if the player places and the gamestate rips at the same time, its a desync
I thought about doing the latter (Having each network owner fix the card) but I'm currently doing mostly Network master driven gameplay logic (To keep it all under one person)
I do admit though, perhaps for a subset of the item phase resolutions I could do it with local owner networking, as long as the following phase start does not contain a Network master command for the item
So if all items are being flipped, if every Network owner flips the card, that is functionally identical to if the Network master did it to them all, but only as long as the Network master doesn't make a move immediately afterwards targeting that item
But going back to my original question, is it possible to forsee/know the result of 2 players attempting to modify the same network object, or to "resync" afterwards and restore normal functionality for both users
has vrchat recently updated something? im getting this error now
I'm going to manually udon sync an array of 100 integers. It's happening once every say... 5 seconds. Is that a lot of data to network sync or negligable in the grand scheme of things?
For context I was about to optimize it by combining the integers through a series of modulo calculations... and then I thought, "is 100 ints even a amount of traffic worth worrying about?"
So the per second limit is 11kb, you'd be doing roughly 4kb, which is around 1/3rd of the available bandwidth for the second it syncs not counting user scripts, I would look into other options like using bytes, shorts, or compressing if possible just to free up bandwidth for other things, but its not the worst thing tbh
If this is literally the only data intensive thing that will ever be used, then don't worry
If there is anything that would bring you up to 8kb per second in any sync interval... I'd err on the side of caution if everything must sync and network clogging is not a viable option
it's ~0,4 kb which should be fine considering that limit (https://udonsharp.docs.vrchat.com/vrchat-api/#integral-numeric-types)
100 x 32 = 3200 bits, or 400 bytes, but if you do once per 5 seconds then it's only 80 bytes per second. (though there's also the overhead of the serialization but at this scale it's negligible) That's a pretty tiny amount and should have no problem. If you do have other expensive things in your world and need to trim it down, then I'd consider using 16 bit shorts or 8 bit bytes before bothering with modulo. The question you should be asking is "what is the range of values that each entry can reach?"
- If these are only going to be between 0 and 255, then you can just use 8 bit bytes, which are integers with a smaller range.
- If you need negative numbers but still won't be going high then you can use sbytes, which go between -128 to 127.
- If you need a bit more range than that but nothing too crazy, you can use 16-bit shorts, which go between -32,768 to 32,767.
- And lastly, if you need even more range but don't need negative numbers, you can use ushorts, which go between 0 to 65535.
There's a lot of options and it's pretty rare to actually need a full, normal 32 bit int because they have the massive range of –2147483648 to 2147483647. If you don't need all of that range, then you can get away with a much smaller data type which will be even cheaper to sync.
But again, this isn't super critical to do when you're at the scale of only 100 synced once every 5 seconds. Reducing the data type would make it sync faster but it won't dramatically change the overall story
Where are you getting the 32bytes from? I assumed it's 4byte according to the docs
Also sidenote we need to account for the fact they are doing a 4kb manual serialization every 5 seconds
I'm not following, where's the 4kb coming from?
While that averages out to like 0.8kb... its still actually a 4kb serialization
Ah did 1000 not 100
400 bytes
Kyro's numbers were most correct
Ye nvm Kyro wins the day here
400 bytes is already tiny, once per 5 seconds is basically nothing
Ye if it was 4kb then what I said would apply
400 bytes... eh
Overhead isn't that bad I'd be suprised if overhead is more than 10 bytes
it's more than you'd think because of byte alignment in the serializer
So ye entirely negligable, if their budget breaches 8kb per second in a worst case scenario I'd tell em to optimize
Is that per variable or per object?
per variable
Fucking christ
That really could use some optimization at somepoint in the future
(The really far future)
It makes the serializer run fast, which of all the places we have to worry about, fortunately serializer performance is not one of them. So at least it's working
but yeah it is painful sometimes
Ye ya'll got more to worry about than that
Let it be a in 3-5 year goal maybe
Whenever actually improving networking becomes a priority again, atm its pretty stable with manual ngl
improving networking is always a priority, but it's very delicate work with very few number of engineers
Oh absolutely
Its low priority its why I do mean it can wait
I'd rather get age verification, persistance a more stable, Soba, ect
yeah but those are all different teams
This arguably at best would reduce your backend networking costs marginally (maybe 3-5% at most?)
I won't presume what Network Engineers might be working on, small team and overlap
I think for the end user the cost per variable serialization is rarely accounted for
(Or needs to)
This is great information
I'm syncing up a procedurely generated grid map. I can probably go a bit crazy then. 256 tile states will be more than enough so I can probably have it syncing up more like a 1000 tiles with no issue.
yesss and if you don't need the full range from -2,147,483,648 to 2,147,483,647 of 2^32 (or uint) you could also try to sync with short (-32,768 to 32,767) or ushort (0 to 65,535) which costs 2 bytes instead of 4.
but if its only 256 states per tile, go with a byte - thats 1/4 of a int
yee exactly
Comes down to how many tiles I want to make.
I'm really miffed that unity terrain can't be rotated
So I'll have to make seperate tiles for different rotations.
@frozen igloo would you per chance know why VRC Player Object spits this error out on someone leaving ?
Are your udonbehaviours doing something that might trigger that in onplayerleft? Is this error causing any noticeable problems? Does it happen even if you do nothing in your udonbehaviours?
this is the only thing im doing. i have a seperate list of player objects that is just used to reference the current player objects in the world. and all it does it removes it locally from their own list.
otherwise im doing nothing @frozen igloo
and for reference im doing no ownership transfers.
afaik the player related player objects will be deleted automatically by the server. That's probably the reason you can't find them- they don't exist anymore
well. the error comes from trying to locate a owner of an object i dont really do anything with. without removing it manually it ends up giving me an position update error because i am trying to access something that no longer exist.
for reference this happen every frame
and it causes a Null reference even if a player leaves which suggest the object is not removed fast enough. so i have to manually make sure the values in the object is null so it does not trigger it
@frozen igloo it seems to do it quite alot even. but the problem is that nothing actually seems to break or cause issues from it? also the CatchManager is a NoVariableSync so nothing syncs in there. but the Object the script is on is a Child of the Vrc Player Object.
if it's not causing any issues then it's probably just a leftover debug log from development that should've been removed
Similar to the message that occurs when you try to take ownership of something you already own
problem is there is no debug logs i have made that does any of this. nor do i take ownership of anything its just a Vrc Player Object that i use with the template function and 6 child objects 3 of them have Udonbeviours the others do not.. and i switched to disable everything on Disable. which works but i still get those logs
debug logs are not just for you :)
elaborate 😄
I mean, don't expect them to be clean and comprehensible all the time. Learn which logs matter to you, and which ones are not your problem
If it's spamming every frame that's a whole other thing, but just when someone leaves? It's fine, don't worry about it
It's not even an error, it's a warning
yea its just doing it when someone leaves. but its odd.
also i assume if we do any delayed event calls by seconds they would be removed on a vrc player object that triggered it to be removed to yea?
yeah, though I wouldn't be surprised if that triggered a warning or error that you can ignore
okay. is there an upper limit to how many objects a person can have owner over?
considering i already have 7 objects each person owns
with 64 slots in an instance.
for reference last sunday so two weeks ago my things worked fine and held at 47 people no issue. but this sunday things broke for some reason and i had not done any updates.
hence why i looked into that warning
Only hard limit on number of playerobjects would be coming from network IDs, in which case it's in the thousands. If your stuff just randomly breaks beyond a certain point then the most likely possibility is that your code has race conditions which are stable under normal conditions but with many people, something takes longer which makes a thing happen out of order and is unaccounted for
yeea. no i am not near that lol
7x64 is 448. and then some other objects
i think in total there is 500 objects in the world.
where the Master owns obviously all objects apart from those Vrc Player Objects
each player has two objects that has a Manual and NoVariable sync. other things are None.
the Manual one is the one that syncs
the NoVariableSync is just for sending a custom network event
and non of those things happen that often
How do I check for a custom named bone using GetBonePosition?
I think I need to do Vector3 CustomBoneCheck = something.GetBonePosition(somethingelse.bonename); but I'm not sure
if it's not a standard HumanBodyBones.bonename do I have to specify some bone definition variable?
Is there a way to declare a bonetype?
You can only get the core bones from an avatar. The ones already defined in HumanBodyBones
Whats the point of being able to target the owner with a custom network event, when only the owner is allowed to send them?
anyone can send a network event, it's only the Owner that can update synced variables and call RequestSerialization
mmm ill try again then maybe my previous project file was bugged, beacuse last time I tried to have a non owner of an object send a network event to the owner, that would ask them to run a function that would end up adjusting one of the sync vars I got a log in the vrclient debug menu 3, that "Non owner is not allowed to send networked events"
Non-owners are absolutely allowed to send networked events.
just to clarify Im talking about the network owner of the udon behaviour sending the networked event and not the instance owner. is that still the case for that?
yes. you interact with a button that someone else owns - network event gets sent to the current owner of it
allright thanks. the error must have been from somthing messed up in the project file. There was a ton of other weird things going on too. been rebuilding it from scratch.
Yeah if you send it to the owner, that works. But if you try to send it to all without being owner, then it won't do the call
that isn't true, any player can call SendCustomNetworkEvent, and target either Owner or All
SendCustomNetworkEvent anyone can do. howver only Owner of an object can RequestSerialization and sync variables.
"web socket exploded" is it possible I did somthing to cause that?
is there a limit to how many serilizations, ownership transfers you can do at one time. Beacuse I did about 100 transfers , 2 events and 6 serilizations on a button click. and it seems to have broken the networking framework compleetly. one of the On deserilizations is in a endless loop of being called but not actualy getting any of the data
its only actualy 300 bytes of data moving at one time. its just split off so that later in the game we only need to use network trafific for parts that actualy change
It's not a sign of something your world did, it's more likely a sign of your network being unstable. But also it happens all the time and there are systems to restart websockets dynamically as needed
so found the issue, There was a loop somehwere that loops on the number of cards in the deck. and one of the those signals would modify the length of cards during the sync at the same time the length is being evaluated on the remote client. causing the whole thing to bug out. Adding a small delay to one of the functions being run on the server fixed the issue for the remote clients
i do question your need to 100s of ownership transfers. its abit extreme
It's a one time transfer at the start of the game. I could reduce the number of objects but it would increase the avg bandwidth used by at least triple. Because udon behaviors update all sync variables during sterilization. So for example we don't need to update all 40 or so remaining cards in the deck if a player moves a card from their hand to the table. So that's one example of where things have been broken down into multiple objects.
However scince its a card game that allows players to make moves Evan if it's not their turn we want a single computer to manage all of the game state to prevent data loss.
And the reason I don't just leave it to the instance owner is there could be many instances of the game table in a given world so we don't want to force all that traffic on a single host. Rather each game table has its own host
well i dont see how it would triple the amount of data required. if you have 40 cards you do not need to sync those 40 cards. you can sync a card that was played. and have it add that card to a collection of played cards per player on a local end only. but only needing to sync the card that was just played.
obviously you still need to transfer it to the one doing so.
judging from the fact its a card game. there isn't to many states that needs to sync. just what card is played, who played it. whos turn it is etc.
It's because each card needs to have its front face hidden when not in play and not in position of the local client.
And it's a 2 to 4 player game. Any player can make moves potentially at any time. Sometimes simotainously and each card played modifies what cards are legal for all other players. But each player has differing conditions based on where they fall on the current cycle
So it needs to be listening and potentially processing input from all 4 players at all times
That's why each players hand is it's own object
So we don't sync all 4 hands again if only one player made a move
And we do need to sync all 52 for position and deck order when the decks is shuffled at the start
The 52 cards don't use up most of the data btw. They don't need updating very often
It's mostly the arrays keeping track of where they should be in sequence
Every card moved is 1 byte of traffic
Actually that's kind of wrong it's allot more.
Unless I'm mistaken there's nothing like that still
