#udon-networking

1 messages · Page 15 of 1

wooden saddle
#

Is 2mb world can't be lagg to

#

im on SDK 3.7.4

#

dealing with thises problem for 4 days naw

strange gyro
#

Try turning off Allow Collision Ownership Transfer on VRCObjectSync in case something keeps trying to take ownership of it

wooden saddle
#

odd naw my VRCPickup dont show anything after i did that

strange gyro
#

That's weird

#

Any errors in the console?

wooden saddle
#

Nop

#

Ok i upload to world but it did not fix it same thing

strange gyro
#

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

wooden saddle
#

Y i don't know is odd

#

How can I reinstall sdk from project clean i think something got corrupted

strange gyro
#

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

finite sierra
#

well. yea so i asked Phasedragon before and its because apprently it runs the actual c# code outside udon.

finite sierra
#

that to but also just in general its slow AF

strange token
wooden saddle
#

ok reinstall all but it did not help

#

@strange gyro

frozen igloo
strange token
#

ohhhh, I see

frozen igloo
#

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.

finite sierra
#

@frozen igloo question then. would the SendCustomEventDelayedFrames be more optimal to run then PostLateUpdate for instance? say every frame?

strange gyro
#

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

plush tiger
#

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;
strange gyro
#

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

plush tiger
#

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

tulip sphinx
#

delayed events allow you to select timing

strange gyro
#

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
    }
}
plush tiger
#

Ah so the phase can be selected, I have not paid enough attention to the delayed event calls signatures

normal sonnet
#

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

finite sierra
stone badger
lone zealot
# normal sonnet Does a change in ownership require a request serialization after? I always ass...

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".

normal sonnet
wooden saddle
#

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 ?

obsidian python
wooden saddle
#

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

obsidian python
wooden saddle
#

no

#

give me in idea how would you do it ?

obsidian python
#

depends on how many objects you want to spawn, if you need a lot, then you need an object pool

wooden saddle
#

or only way do it is to telport items from corner of the world?

#

is only spawns 2 ojects for naw even 1

obsidian python
# wooden saddle give me in idea how would you do it ?

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

wooden saddle
#

i se so the have to be in world that is my problem @obsidian python

obsidian python
wooden saddle
#

@obsidian python thanks naw that i know that i can get it working i was at it days naw

violet mist
#

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?

frozen igloo
# violet mist so over the past year or so, my community has reported a somewhat rare issue tha...

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

violet mist
#

in case it's helpful, here's every component on one of the pickups (they're all identical)

obtuse echo
#

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.

violet mist
# obtuse echo I had the same issue and it drove me insane lol There definitely is something up...

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

violet mist
#

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

obtuse echo
#

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

violet mist
obtuse echo
violet mist
#

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. ?

tulip sphinx
#

traffic is limited by the emitter ie owner of object

#

with one person owning every pickup its 200 being unreliable shit already

finite sierra
heavy spindle
#

Even in less pickup heavy worlds, if they all are owned by a user on a mobile platform, the networking strain can be immense

frozen igloo
#

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)

heavy spindle
#

At 200+ non moving pickups, I'd start to question design decisions, but yeah

tulip sphinx
#

200 objects in a pile were causing suffering on test client. its 5hz, not every frame.

frozen igloo
#

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

tulip sphinx
#

i tested with like 5 pickups laying on the floor for ages, never went below 5hz

finite sierra
#

@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?

frozen igloo
#

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

finite sierra
#

@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.

frozen igloo
#

Yeah sounds like what you have is a bit too complicated for me to know for certain what the problem is

finite sierra
#

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

obsidian cedar
#

It shouldn't discard anything, Manual sync SHOULD be reliable

finite sierra
#

hmm.

obsidian cedar
#

And anything you queue up in NetCaller should also not discard AnimuThinku it should get queued up and get sent in next NetCaller "packet"

finite sierra
#

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

frozen igloo
#

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?

finite sierra
#

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

frozen igloo
#

I am not remotely familiar with the specs and limitations of net event caller

finite sierra
#

@obsidian cedar maybe you are able to enlighten us abit 😄

obsidian cedar
#

Maybe when Sona releases

frozen igloo
#

yeah that sounds like what you're running into then heh

obsidian cedar
#

Though I do swear I heard someone say Manual Sync was reliable and should make sure all syncs are delivered AnimuThinku

frozen igloo
#

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

obsidian cedar
frozen igloo
#

that's a pretty big convo, but yeah when Euan says reliable he means eventually reliable, not every intermediate state reliable

finite sierra
#

@frozen igloo i assume the traditional way has a way to counter act that? like the package overwriting

frozen igloo
#

Could you elaborate on what you're asking?

#

not sure what you mean by traditional way or package overwriting

finite sierra
#

well i mean not using the thing miner has but the Request serialization and other things.

frozen igloo
#

afaik miner's thing uses manual sync/requestserialization which is what this is all about

frozen igloo
frozen igloo
#

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

finite sierra
#

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

frozen igloo
#

Please clarify if you are referring to vrc network events or net event caller events

finite sierra
#

yea sorry vrc network events

frozen igloo
#

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

finite sierra
#

okay so i missunderstood that part. what exactly can cause interruptions then?

frozen igloo
#

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

finite sierra
#

oookay.

#

so i may not have run into that issue atleast.

frozen igloo
#

just remember that miner's net event caller is using manual sync

finite sierra
#

yep yep

frozen igloo
#

so it is subject to the interruption problem

finite sierra
#

hmm

#

have u seen it happen before?

#

like the interruption problem?

frozen igloo
#

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

finite sierra
#

in the server case here i assume u mean just letting the vrc events handle it self?

frozen igloo
#

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

finite sierra
#

okay

#

but i do assume i have to not have my things have the NoVaribleSync ?

frozen igloo
#

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

finite sierra
#

alright.

#

im gonna try to do that

frozen igloo
finite sierra
#

does it still receive the late joiner thing?

frozen igloo
#

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

finite sierra
#

okay so should set them to manual then i am guessing

#

to have them buffered

frozen igloo
#

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

finite sierra
#

oh yea i know MVVM just never really thought about being able to use it that well in a udon situation

frozen igloo
#

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

obsidian cedar
frozen igloo
#

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

obsidian cedar
#

Well then we need SendCustomNetworkEvent with parameters official implementation 😆

finite sierra
#

that would really be nice

frozen igloo
#

yeah, the barriers towards doing that are relaxing. It'll happen eventually

obsidian cedar
#

If Sona doesn't have it on release I'll be lil sad tbh but sadly expecting that it won't 😅

finite sierra
frozen igloo
#

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 Sigh

obsidian cedar
#

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

finite sierra
#

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

obsidian cedar
#

NetCaller is basically RPC system and it shouldn't reallly store state

finite sierra
#

its prob also gonna solve all my problems regarding rejoin lol.

obsidian cedar
#

Just have a Manually Synced Variable that syncs the late joiner data

finite sierra
#

yee

#

good thing i am doing object based ownership. so it should be fairly easy to do.

frozen igloo
finite sierra
#

@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

finite sierra
#

@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

frozen igloo
#

add onpostserialization and log result success

#

if something is preventing it from sending, ondeserialization won't happen and onpostserialization will say it failed

finite sierra
#

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

frozen igloo
wooden saddle
#

is there way syc all items in world so new players can se whin the join changes?

#

what old players did

heavy spindle
#

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

wooden saddle
#

i wanna on new player join all objects show location and state like old users se like most wolrlds have

wicked yarrow
#

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?

vapid pagoda
#

Though i dont know if it happens before or after when using OnPlayerLeft

wicked yarrow
# vapid pagoda You may want to use OnOwnershipTransferred instead of OnPlayerLeft, also the lea...

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

finite sierra
finite sierra
#

@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.

plush tiger
#

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)

finite sierra
#

and even without modifiying it. the object would just disappear. oh and i use PostLateUpdate for it. since thats accurate to the latest position

tulip sphinx
#

tracking data or bone position?

finite sierra
#

well Bone Position for the head.

tulip sphinx
#

bone position is 0 for non humanoids and shouldnt be used

finite sierra
#

its fine its a humannoid in this case

tulip sphinx
#

tracking data is camera locally but head bone remotely

finite sierra
tulip sphinx
#

(remote clients cant look too much up or down)

finite sierra
#

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

tulip sphinx
#

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

finite sierra
#

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

finite sierra
#

@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?

strange gyro
#

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

finite sierra
#

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

finite sierra
#

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.

wooden saddle
#

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?

thick quail
#

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

plush tiger
#

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)
thick quail
#

yk what

#

thats acc genius XDDDD

#

tysm ill lyk if it still hass issues

thick quail
plush tiger
#

you need to make an int variable and math it

polar dust
#

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?

tulip sphinx
#

no

polar dust
faint osprey
#

question: how can i make so that something automatically enables itself locally after an user has been inside an instance for 1h?

tulip sphinx
#

Start - Sendcustomeventdelayedseconds 3600🤷‍♂️

severe gate
#

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.

tulip sphinx
#

show smth

severe gate
#

this is what im currently trying to use

tulip sphinx
#

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

severe gate
#

so something like this

faint osprey
#

that was way easier than i expeted tysm

faint osprey
tulip sphinx
#

wha. no

#

i just named nodes/methods

finite sierra
#

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

finite sierra
restive cliff
#

ok say that we have found the B button in that spreadsheet. What would you like to do with it?

finite sierra
#

eh just something simple like bringing up the HUD for a player. that isnt on all the time

restive cliff
#

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?

finite sierra
#

yea i know. ngl i was tired the first time i was reading it. found it now lol.

limber light
#

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.

stone badger
limber light
sturdy hornet
#

how would i do a synced instigate?

limber light
normal sonnet
#

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"

limber light
sturdy hornet
#

how do i take a players name saved in a string to get their position cause i cant plug those two together

limber light
strange gyro
#

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

sturdy hornet
#

nvm figured it out

normal sonnet
#

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

stone badger
limber light
# normal sonnet

Hmm i thought i read the exact opposite that syncing variables was slower, but your text there clearly contradicts that idea.

strange gyro
#

Manual sync is definitely the fastest option

#

But several seconds for network events is abnormal

stone badger
strange gyro
#

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

limber light
stone badger
#

In any case you should decide what you need to do and use the option that provides that solution. They are not interchangeable.

finite sierra
#

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.

finite sierra
#

hmm. seems like Every udon behaviour has a overhead cost of 12 bytes when being networked.

hoary frost
#

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

obtuse echo
obtuse echo
hoary frost
#

the onvarchange does work when im setting it to true

obtuse echo
obtuse echo
#

I'd be curious how other values like ints would respond...

hoary frost
hoary frost
#

it usually works on other scripts, dunno why this one doesnt want to

hoary frost
#

but the local player does

finite sierra
hoary frost
hoary frost
#

maybe its a graph bug

hoary frost
#

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

finite sierra
hoary frost
#

hmm, maybe im calling serialization too early, and it confuses it.
Ill do some more testing

obtuse echo
hoary frost
#

although i could post only the relevant parts if you want

brisk magnet
#

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.

heavy spindle
#

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

finite sierra
#

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

jovial parrot
#

anyone know what box to set a objects material with? like, on interact i want this objects material to change to another mat?

twin portal
#

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

jovial parrot
#

thank you!

jovial parrot
#

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?

twin portal
#

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

tulip sphinx
#

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.

brisk magnet
#

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

strange gyro
#

I think that would be over the serialization limit

honest lance
#

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.

strange gyro
#

Is it using continuous sync?

honest lance
#

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

brisk magnet
#

Am I supposed to set the value of a and b to themselves inside of ondesrialzation?

honest lance
#

no, that wouldn't accomplish anything

#

is your udonbehaviour using manual or continuous synchronization?

brisk magnet
#

I've got a version that's manual with the request lines added and one that's continues but they both behave the same

strange gyro
#

Well continuous is maxed out at 200 bytes per serialization which an int[52] would be over for sure

brisk magnet
#

Yea so manual will be the one i should move forward with for sure

honest lance
brisk magnet
#

Are these limits shared between scripts?

honest lance
#

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

brisk magnet
#

What it's 2 objects but one is a child of the other

honest lance
#

that should be fine then

#

but do check if removing the continuous one helps, as it may be saturating the shared 11kb/s limit

brisk magnet
#

And let's say I have 7 synched vars is there a way to request sterilization just for one specific value?

brisk magnet
honest lance
#

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

brisk magnet
honest lance
#

ah, so the 52 element array is probably the deck order then

brisk magnet
#

Yup

honest lance
#

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%

brisk magnet
#

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

honest lance
#

there are implicit conversions between byte and int

brisk magnet
#

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

honest lance
#

256 if you don't need to represent the lack of a card

brisk magnet
#

I think I'm just going to opt to resize the array as cards are removed from the deck and given to players

strange gyro
#

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

honest lance
#

assuming you can guarantee random numbers will always be drawn in the same order

strange gyro
#

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

honest lance
#

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

strange gyro
#

You only need to shuffle the array on deserialization

#

And only when the seed changes

brisk magnet
#

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

honest lance
#

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

brisk magnet
#

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

honest lance
#

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

brisk magnet
#

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

brisk magnet
#

Like the pool table prefabs etc

honest lance
#

why would playerobjects not be asset friendly?

brisk magnet
#

Arnt there limits to how many you can have per player?

honest lance
#

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

finite sierra
honest lance
#

so avatar parameter sync affects the bandwidth available to udonbehaviour sync?

tulip sphinx
#

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

finite sierra
#

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

frozen igloo
#

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.

finite sierra
#

@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

frozen igloo
#

If you're alone, a lot of the internal stuff like player position and ik doesn't run to save bandwidth

finite sierra
#

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?

north thistle
#

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.

frozen igloo
#

In theory that should be true, but of course if you implement things like onplayerjoin resync then that gets thrown off

finite sierra
rain patio
#

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.

rain patio
#

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.

rain patio
#

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

iron burrow
#

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

meager meadow
iron burrow
hoary frost
twin portal
#

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

hoary frost
#

i see, makes sense

cloud plank
#

Is it possible to reach across VR platforms?

twin portal
#

no

stone badger
cloud plank
#

Im VERY new so any experience or critics are welcome...

cloud plank
#

Does UdonSharp have any kind of support for WebSockets? ...or if OSC can be used in conjunction with WebSockets...?

cold laurel
finite sierra
cloud plank
#

I need help testing...

broken rampart
# cloud plank Does UdonSharp have any kind of support for WebSockets? ...or if OSC can be used...

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)

hearty vale
#

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?

tulip sphinx
#

yep

finite sierra
#

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

twin portal
# finite sierra is there an actual definitive answer as to how much data we actually have. and w...

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.

finite sierra
twin portal
#

it was last updated Dec 11.... but ok

finite sierra
#

yea i know. but trust me its not correct.

twin portal
#

@frozen igloo you got any updates on this subject

frozen igloo
#

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"

finite sierra
cerulean zealot
#

I rely on Serializaton of Variables. Integers, and then those integers call for a custom event locally on everyone client.

jolly flare
#

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.

finite sierra
twin portal
# finite sierra i am not saying NetworkedEvent.. and also cant use Request Serialization for eve...

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

finite sierra
#

plus your wasting alot of resources requesting serializations if nothing is updated

twin portal
#

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

finite sierra
#

eh. no. not when its not required.

#

if its only local operations

twin portal
#

well of course don't do things when they aren't required. I feel like that is implied

finite sierra
#

thats why i mentioned it was not SendCumstonNetworkEvent. it was just a SendCustomEvent.

#

that i find is skipped sometimes.

twin portal
#

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

finite sierra
#

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

twin portal
#

it's more likely there is an unhandled condition where it won't fire

#

there's thousands of edge cases in everything

finite sierra
#

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.

twin portal
#

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

finite sierra
#

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.

hoary frost
finite sierra
twin portal
#

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

finite sierra
# twin portal like I don't mean to sound rude here but if your conclusion of your code not wor...

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..

hoary frost
#

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

finite sierra
#

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.

twin portal
#

is this a new object for every player? or is everyone transferring and sharing ownership of one object?

finite sierra
#

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

twin portal
#

are these objects PlayerObjects?

finite sierra
#

no

#

i dont use the Persistence thing

twin portal
#

PlayerObjects don't inherently use persistence

finite sierra
#

i know

#

but i dont use PlayerObjects

twin portal
#

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

finite sierra
#

hmm. you are right but my question is are we able to have colliders on them? etc and other custom things?

twin portal
#

yeah they can have anything any other GameObject can have

finite sierra
#

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

twin portal
#

yeah you've just got your own self-made PlayerObjects lol.

#

they should work a lot better for your case

finite sierra
#

to be fair i was making this before playerObjects became a thing.

twin portal
#

that's understandable

finite sierra
#

luckly it should be a fairly easy Retrofit.

#

i hope lol

twin portal
#

do you have these 80 objects already in specific places in the world, or do you place them once a player joins?

finite sierra
#

yea. premade

#

in unity editor.

twin portal
#

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

finite sierra
#

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

twin portal
#

that shouldn't be necessary anymore, as you can just check ownership of the object. Or just store the PlayerID on it instead

finite sierra
#

hmm. the thing is PlayerID is a incrementing ID if i remember correctly

#

its not even unigue?

#

or rather it is

twin portal
#

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)

finite sierra
#

i believe its 1.

twin portal
#

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

finite sierra
#

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?

twin portal
#

yes

finite sierra
#

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
twin portal
#

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

finite sierra
#

perhaps.

#

but i will find out lol.

twin portal
#

A good starter test for PlayerObjects is to just make a VRCPickup

#

then you can kinda see how they behave per player

finite sierra
#

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

boreal cradle
#

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)?

finite folio
#

Has anyone encountered such a problem?

twin portal
#

yep

#

you need to regenerate Network IDs

#

I should really keep the screenshot for it handy....

#

Open the Network ID Import and Export Utility, then in this window select Regenerate Scene IDs

#

@finite folio

finite folio
#

Thank you, I'll try in a while.

neon kestrel
#

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

hoary frost
tulip sphinx
#

its utc sync for looped animators, wont work for navmesh

hoary frost
#

oh

tulip sphinx
#

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.

neon kestrel
#

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

strange token
#

What causes a "Serialization failed for script" warning? Is it an issue of the data being too large to send, or something else?

twin portal
#

I think so, can also happen if you try to sync an uninitialized array

finite sierra
strange token
#

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 👌

finite sierra
lone zealot
strange token
#

I’ll give that a shot too!

twin portal
#

dunno why this info is under the RequestSerialization info instead of like, in the UdonSync page, but this is the general Networking doc

strange token
twin portal
#

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

uncut pivot
#

I am having a problem with the SendCustomNetworkEvent, Is this not how you use it? The PlayAnimation gets called but not the PlayNextSegment.

twin portal
#

so you're seeing the "PlayAnimation Called..." message, but not the "Resuming animation..." debug message?

uncut pivot
#

I pressed both buttons. Thats all i get.

tulip sphinx
#

uh wheres ResetAnimation function tho

#

its ResetAni

twin portal
#

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

uncut pivot
#

Still not working can upload and do a live test too. Here is what i see in unity.

twin portal
#

what sync mode is your script set to?

uncut pivot
#

This is at the top... Is this not good? "[UdonBehaviourSyncMode(BehaviourSyncMode.None)]"

#

Guessing that could be the problem. smh

twin portal
#

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

uncut pivot
#

Well that was stupid. Thank you for the help anyways.

#

I just removed that line at the top.

twin portal
#

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

uncut pivot
#

I like setting them in the inspector for the most part lol.

stone badger
#
[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

strange token
#

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? 🤔

frozen igloo
# strange token I'm back with more confusion with arrays. I'm using a FieldChangeCallback on a s...

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

strange token
#

eugh, that's horrible lol

#

thanks for all the detailed information!!

frozen igloo
#

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

strange token
#

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

blissful lark
#

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

frozen igloo
#

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

blissful lark
#

I mean now sure because vrc broke that contract

#

Now it fires when the array size changes

frozen igloo
#

it was not a contract, it was behavior that was never defined in the first place

blissful lark
#

So it's more weird honestly

#

No behavior in udon has ever been defined, that's why all the high end creators leave

frozen igloo
#

sure it might be more weird but if it's gonna be weird, I'd prefer the weird that people don't rely on

blissful lark
#

Vrc aggressively is against providing a reliable system

frozen igloo
#

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

blissful lark
#

Sure, failing that the less weird interaction is to just do a bit of redundant work when the array doesn't change

frozen igloo
#

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

blissful lark
#

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

frozen igloo
#

that's an opinion though

blissful lark
#

I mean this discussion was started by someone assuming it works the way it did

frozen igloo
#

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

frozen igloo
# strange token How exactly do you use OnDeserialization to detect changes to an array? I have y...

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

strange token
#

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

frozen igloo
#

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

hoary frost
finite sierra
blissful lark
#

documenting would be defining behavior

#

at least theoretically

foggy jackal
#

yeah there's a surprising lack of documentation on anything udon

lone zealot
#

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 😔

finite sierra
#

you can essentially protect everything in C# and not expose anything to external.

#

also c# has the Proxy pattern.

lone zealot
#

The difference is that this can make existing code reactive, by building your proxy around it.

finite sierra
#

which sounds like a trap for performance. hence why its not there.

#

everything in c# is reactive through anyway..

lone zealot
#

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

finite sierra
#

when you mean property. give an example here.

#

do u mean like a get setter?

lone zealot
#

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

finite sierra
#

yea? and if u change that anything else that uses it knows it changed.

#

unless u mean u want it to notify it changes>?

lone zealot
#

I think you dont understand what "reactivity" means.

#

Yes. Thats what that means.

finite sierra
#

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

lone zealot
#

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

finite sierra
#

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.

lone zealot
#

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)

finite sierra
#

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.

lone zealot
#

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.

finite sierra
#

its prob something they did to reduce the networking cost.

lone zealot
#

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.

finite sierra
#

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.

lone zealot
#

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

finite sierra
#

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.

steady fulcrum
#

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

twin portal
#

It is fairly difficult, but not impossible

steady fulcrum
#

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

twin portal
#

good luck with that

#

youre gonna be reading a looooooot of manuals

steady fulcrum
#

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?

steady fulcrum
#

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

steady fulcrum
#

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

tulip sphinx
#

how tf you ended up with no descriptor in the first place, theres fing template loaded on new project

steady fulcrum
#

I don't use templates, I make everything manually. I will look into them in the future though

tulip sphinx
#

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.

steady fulcrum
#

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)

foggy jackal
#

it's easy enough to drag the world prefab into the scene though

tulip sphinx
#

hows hub is not a thing when its 2018, like do stuff the proper way ie through vcc and stuff to get relatable support

steady fulcrum
foggy jackal
#

yep, there is

finite sierra
finite sierra
steady fulcrum
finite sierra
steady fulcrum
#

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

finite sierra
finite sierra
steady fulcrum
#

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?

silver hill
#

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?

frozen igloo
# silver hill Could someone help me out this is driving me crazy, So Ive made a world and it h...

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

silver hill
#

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?

frozen igloo
#

Is this entire structure just to unlock a door? You should do that with synced variables, not network events

silver hill
#

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

frozen igloo
#

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

silver hill
#

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

frozen igloo
#

I mean yeah that's what an isowner check is good for

#

Only one person is the owner, they determine state

silver hill
#

Oh yes good idea, Il use that I think

frozen igloo
#

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

silver hill
#

Will do, thanks

foggy jackal
#

literally setting this up today, thanks for the timely advice 🙂

frozen igloo
# silver hill Oh yes good idea, Il use that I think

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.

foggy jackal
#

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.

boreal cradle
#

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.

tulip sphinx
#

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.

boreal cradle
#

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

tulip sphinx
#

whats the point of trying for 250 syncs with 80-150ms ping and 400ms audio delay tho.

boreal cradle
#

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

finite sierra
#

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

boreal cradle
#

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 ratl

#

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.

finite sierra
#

Eh. No. Since the overhead is 12 bytes. On top of the string.

boreal cradle
#

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

finite sierra
#

Eh. I don't know what your trying to sync that requires 620 bytes worth of a string.

boreal cradle
#

welp, some 100 6B events plus some overhead xd

finite sierra
#

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?

boreal cradle
#

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.

finite sierra
#

That would result in minimum of 2 characters per byte. In the array.

boreal cradle
#

...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.

finite sierra
#

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.

boreal cradle
#

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 vrcSkull

frozen igloo
#

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

finite sierra
#

Well most if not all turns it into a hex. Again if you use to string.

#

On a byte array atleast

frozen igloo
#

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

finite sierra
#

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

frozen igloo
#

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.

finite sierra
#

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

strange gyro
#

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;
frozen igloo
# finite sierra That does make me wonder if it's possible to reduce 2 longs, a byte and bool. Fu...

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.
blissful lark
#

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

frozen igloo
#

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).

finite sierra
#

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.

finite sierra
#

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

frozen igloo
#

There's a relay server that routes, processes, and caches traffic between users in the instance

finite sierra
#

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

frozen igloo
#

to the best of my knowledge

finite sierra
#

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?

frozen igloo
#

that's the unknown right now

#

It's something I want to look into

finite sierra
#

hmm gotcha

boreal cradle
#

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...

twin portal
plush tiger
#

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

twin portal
boreal cradle
#

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.

foggy jackal
#

betas will likely become releases though

plush tiger
#

Its not a vrc beta

boreal cradle
#

We might not even get that beta though, depending on how soon they plan to introduce soba

foggy jackal
#

oh true

boreal cradle
#

I have a feeling that while u# wil stay supported, its active development will come to a halt when soba does release.

twin portal
#

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

boreal cradle
#

same

strange token
#

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

boreal cradle
#

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

finite sierra
finite sierra
#

also 257 resize of array a second? thats way to much and it will cripple udon guranteed.

boreal cradle
#

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

finite sierra
#

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

boreal cradle
#

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.

finite sierra
#

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.

boreal cradle
#

first two will most definitely be part of it, no doubts from my side

finite sierra
#

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

strange token
#

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

vapid pagoda
strange token
#

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

high elm
#

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.

twin portal
# high elm so i put this little animation trigger randomizer together and am wondering what...

Whenever I'm working with randomization and need to network it, here's a technique that works pretty well:

  1. The Owner (not master) generates a random value.
  2. Store this value as a synced variable.
  3. Owner then uses this value to set what is needed, then calls RequestSerialization. Or, use the variable's OnValueChanged event.
  4. 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.

strange token
#

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

cerulean zealot
#

@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.

high elm
#

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?

twin portal
high elm
twin portal
#

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

high elm
#

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

twin portal
#

ok makes sense

high elm
#

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

twin portal
#

that's right, you'll always need the owner to run it for themselves

high elm
#

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?

high elm
#

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?

twin portal
#

ah that's because OnPlayerJoined runs for everyone

#

so when the owner joins, this evaluates to true

#

even if you aren't the owner

high elm
#

oh god

twin portal
#

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

high elm
#

like so?

twin portal
#

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

frozen igloo
strange token
#

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 🤔

frozen igloo
strange token
#

Thanks for confirming! I can unshittify some code I wrote today to make rewriting it easier :D

timber parcel
#

playerID=0 is lurking in all of our VR instances. Watching, waiting.

Sometimes a glitchy mirror will show it, for just a moment

boreal cradle
#

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 :/

finite sierra
#

can be two different users

fleet arch
#

There's also some range reserved for players too (world network IDs)

boreal cradle
# finite sierra master != InstanceOwner necessorily.

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.

raw umbra
#

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

frozen igloo
#

either that, or just have the current owner do the phase change, whoever that might be

raw umbra
#

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

finite sierra
#

has vrchat recently updated something? im getting this error now

timber parcel
#

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?"

raw umbra
#

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

iron burrow
frozen igloo
# timber parcel I'm going to manually udon sync an array of 100 integers. It's happening once ev...

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

iron burrow
frozen igloo
#

integers are 32 bytes

#

oh wait you're right I'm mixing up bits and bytes my bad

raw umbra
#

Also sidenote we need to account for the fact they are doing a 4kb manual serialization every 5 seconds

frozen igloo
#

I'm not following, where's the 4kb coming from?

raw umbra
#

While that averages out to like 0.8kb... its still actually a 4kb serialization

#

Ah did 1000 not 100

#

400 bytes

frozen igloo
#

Kyro's numbers were most correct

raw umbra
#

Ye nvm Kyro wins the day here

frozen igloo
#

400 bytes is already tiny, once per 5 seconds is basically nothing

raw umbra
#

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

frozen igloo
#

it's more than you'd think because of byte alignment in the serializer

raw umbra
#

So ye entirely negligable, if their budget breaches 8kb per second in a worst case scenario I'd tell em to optimize

raw umbra
frozen igloo
#

per variable

raw umbra
#

Fucking christ

#

That really could use some optimization at somepoint in the future

#

(The really far future)

frozen igloo
#

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

raw umbra
#

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

frozen igloo
#

improving networking is always a priority, but it's very delicate work with very few number of engineers

raw umbra
#

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

frozen igloo
#

yeah but those are all different teams

raw umbra
#

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

raw umbra
#

(Or needs to)

timber parcel
#

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.

iron burrow
#

but if its only 256 states per tile, go with a byte - thats 1/4 of a int

timber parcel
#

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.

finite sierra
#

@frozen igloo would you per chance know why VRC Player Object spits this error out on someone leaving ?

frozen igloo
#

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?

finite sierra
#

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.

iron burrow
finite sierra
#

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

finite sierra
#

@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.

frozen igloo
#

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

finite sierra
frozen igloo
#

debug logs are not just for you :)

finite sierra
#

elaborate 😄

frozen igloo
#

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

finite sierra
#

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?

frozen igloo
#

yeah, though I wouldn't be surprised if that triggered a warning or error that you can ignore

finite sierra
#

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

frozen igloo
#

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

finite sierra
#

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

thorny ibex
#

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?

plush tiger
#

You can only get the core bones from an avatar. The ones already defined in HumanBodyBones

brisk magnet
#

Whats the point of being able to target the owner with a custom network event, when only the owner is allowed to send them?

twin portal
brisk magnet
cold laurel
brisk magnet
tulip sphinx
#

yes. you interact with a button that someone else owns - network event gets sent to the current owner of it

brisk magnet
#

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.

iron burrow
#

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

twin portal
#

that isn't true, any player can call SendCustomNetworkEvent, and target either Owner or All

finite sierra
#

SendCustomNetworkEvent anyone can do. howver only Owner of an object can RequestSerialization and sync variables.

brisk magnet
#

"web socket exploded" is it possible I did somthing to cause that?

brisk magnet
#

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

frozen igloo
brisk magnet
finite sierra
brisk magnet
# finite sierra 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

finite sierra
#

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.

brisk magnet
#

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

brisk magnet
#

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.