#udon-networking
1 messages · Page 30 of 1
Have you looked at the example script in example central for a join zone?
Aren't you requesting serialize and having deserializing in different scripts? That isn't going to work.
I think that will work if they are on the same gameobject
Haven't tested it, though
Try putting your UpdateDisplay and OnDeserialization into your GameManager script and make sure you are using Manual syncing if you arent already, afaik OnDeserialization only works on other clients whenever you actually update at least one UdonSynced variable
why are there base.xyz() in the methods?
and is this dead code?
it's an IDE suggestion at their text cursor
don't include the base stuff for VRC override methods; you'll be calling an empty function and acomplishing nothing but a slight performance cost
i've moved UpdateDisplay to the GameManager script and and am now using OnPlayerTriggerStay. It now works for everyone but the Owner
Hey your original implementation works better in my opinion. The GameMaster's OnDeserialization should have called AudienceZone's UpdateDisplay
does anyone have that overhead spreadsheet calculator
Check da pins
oh neat ty
Is there a good way to test player leaving in clientsim? There's an add remote player button, but nothing for removing
You have to open the clientsims Settings Window and remove player from there.
Uhm my syncedobject is synced until I trigger this respawn button trigger I made when someone is still holding it.
reproduction:
- Hold the object on one user
- Click the respawn trigger on the other
- hold the object again
- see it is no longer synced for either of the players
Do I just add a drop method?
Edit: I tried, but nope that didnt work
there's an event named OnOwnershipTransferred that you could use to force non-owners to drop it when ownership changes, but it'd require your script to be on the object (it seems your script isn't on the pickup/object sync object)
but it's probably more clear or correct to send a network event and have the current owner drop and respawn it themselves
respawn script is on a box mesh that I made a button
the pickup and object sync is on the camera object itself
if you set the sync type of your script to manual you'd be able to do the custom network event version i described
on the respawn?
in the inspector where it says Synchronization: None
i don't have unity or graph open right now, but you'd want to send a custom network event upon interact
the custom network event, when triggered, should use a branch node, the condition being that the person seeing the event is the owner of the gameobject you want to respawn
if they are the owner (true branch), they should drop the pickup and call respawn
as long as the owner is holding, then it wont respawn correctly. otherwise when its not held its not a problem
when they're holding the object they dont need and cannot press the button, which simplifies the logic
you only want it to respawn if it's not held then? you can check that after the ownership check if needed, on the pickup itself, it can tell you if it's currently held
nope thats also not what I meant
i am not sure what you're asking then, sorry
Im just saying: when its NOT held, then the respawn works fine.
when it is held the respawn desyncs the object. requiring a full rejoin to work again
i was suggesting an alternative that forces the current owner to respawn it, which bypasses the idea of the player pressing the button respawning it, which means it wouldn't desync to begin with
personally i think you should call the event directly from the interact, unless you want the logic to differ when it's held vs not held
you'll need to define a custom event with a name and make the custom network event reference it by name, in the eventName field
well if the object is not held, then it doesnt matter whose the owner.
is that not what I already did using the conststring?
i believe it's called custom event, the node
it has an impulse coming from it
you'll be able to name it and select it from the eventName dropdown
atm that will make everyone call respawn, i don't know what that does; you may want to force everyone to drop the pickup when they see the network event and then branch after the custom network event based upon ownership
oh i'm wrong, the network event targets owner
so that should work
actually it targets the owner of your external script
not the owner of the pickup
so you will need the ownership check prior to the respawn, you actually want it to target all
Thank you for the idea.
I do see some input delay between the button click and the camera/object being respawned, but it does not desync.
you could attempt the first approach i described if you want it to be locally instant afaik, but i don't really trust the order of that interaction and it'd require you to place a script on the pickup itself too, to check for the ownership transfer event
honestly I dont really mind.
I just wanted to be able to respawn the camera if it was missing (either left behind or still in someones hand) without it desyncing.
so yeah this input delay is fine.
thanks guys
TBF I think think there is any game where Networking is instant. Networking is a pretty complex topic with many different ways to approach them, all with their pros and cons.
For general usage the way VRC approaches networking seems pretty good. The biggest issues is that VRC's networking being a jack of all trades, it isn't particularly good at specific things.
network id utilities
Hewwo ^^
I'm about to debug my Udon networking shenanigans and I wanted to know if there is a way to do it with the unity debugger ?
My assumption is that it's not possible and that I'll have to upload a private version of the world and ask friends / create a second VRC account
use “build & test”, force non-VR mode, increase client count beyond 1
I'm pretty sure you can also have alt accounts load into a local-only world via the VRC Quick Launcher (go to the VRChat Creator Companion and then Tools)
at the very least it also allows to test late joining even without an alt account
if four players under the same name is fine then build and test is enough, for 2 under one and 2 onder the others (actual vrc accounts) you want vcc launcher
I’ve always hated not being able to have separated names 😅 I see now that you can
build and test supports up to 8 iirc
and that might be the player limit overall for a local world
Also does anyone know whether or not it is possible for Networking.IsOwner(gameObject) to be true within OnDeserialization?
For example can player 1 sync values and then leave; and then player receives ownership after which they receive those synced values?
it should be possible during the window if you set ownership locally and before the actual owner can say no and your ownership goes back to the original owner, which in theory takes at least your RTT + the owner's RTT which can total quite a bit of time; during this time the "actual" ownership is still with the original owner so it should be entirely possible to have received syncs and getting OnDeserialization to fire
There is the possibility they designed it so if someone sees themself as the Owner of an object they reject remote sync values; that way an Owner that tries immedietly setting values after taking ownership doesn't have those values randomly changed on them
I dont know if that is the case but I have certainly experienced something like that, like picking up something and having it glitched back and forth like it is confused about its position
well that's VRCObjectSync I presume, which uses its own special networking
yea I did notice the glitching when transferring ownership
also using object sync
but somehow making my own udon behavior with cont sync was way worse than just using object sync
continuous sync having a smaller limit than manual makes it sound like the worse possible thing lol
Hi, so I asked this awhile back but I want to get confirmation since I am now going forward with implementing networking. For network events, if I have copies of a u# script (think a copy of a network manager class for a board game, several identical boards per world) and i call a newtork event with a named target, will network events know implicitly which script to send the network event to, even though the scripts will have identical names of files/methods/objects, or will I need to figure out some way to discriminate between each table copy i have in the world, i.e. with a table id
The example in mind is
table 1
table 2
literally copy pasted in unity, so same everything
player 1 interacts with table 1, calling SendCustomNetworkEvent(NetworkEventTarget.All, nameof(Foo)). Will this trigger Foo on table 1 and table 2, or just table 1? If not, should I add a variable to discriminate between tables?
just table 1
They are different instances of a same script, you can imagine they being different scripts just with exact same code
Sweet, I was hoping that was how it works
that makes my approach easy
many thanks
You can send network events to a targeted instance of a Udon Behavior, but by default if you dont taget anything it just targets itself, so it is just table 1 in your example
Alternatively if you target table 2, it will just be table 2, but not table 1
When the docs say this, this means across all networking events in a world?
for one client, yes
mandatory watching
#udon-networking message
is there more of this series ?
its one video afaik
How is that video not pinned yet
yipee
pretty sure its global. 100 per second globally.
Global as in for all people in an instance? I vaguely remember testing this to be local only but maybe I rememberd wrong
That makes things complicated if someone wants to do events like rapid gunshots with several people firing at the same time
The docs refer to it as "global" as in, the 100 event limit shared across all UdonBehaviours, but not shared between players
Local clients have literally no way of knowing how many events another player is sending at the moment of them sending it anyway
I swore it was pinned. Did it accidentally get unpinned at some point?
For anyone who's created games that use a single player as the "source of truth" for game logic to prevent cheating or other
How did it go for you?
Is the latency bad?
helo quick question
Where can i get the prefab path/name to use for Networking.Instantiate ?
@marble berry thats not a thing, instantiated stuff cannot be networked on its own
huh
every networked object has predetermined network id on upload
instantiating and syncing stuff consists of some external script doing network part or reusing a pool etc
Should i use object.instantiate to instantiate gameobjects ? it works in editor but not in vrchat for some reason
i thought networking.instantiate was the thing to use to fix that
the script is the thing doing the networking, its just instantiating a mesh for a visual
yes. In Udon its VRChat.Instantiate node but in u# iirc you just call Instantiate
hmm, i have to figure out why insantiate doesnt do anything in vrc
i know the code runs because it logs right before and right after, but the object isnt instantiated
its a method that is called through network and instantiates one mesh, it should work i think, unless instantiating is affected by networking one way or another
also yes its U#
instantiating runs locally for everyone who received an event, thats the idea
yeah thats what im going for
@marble berry are you sure your object to instantiate is valid for everyone?
what do you mean ? that its instantiating the right object/isnt instantiating nothing ?
well maybe they dont have an object reference set
it should work but im not sure how to test that, it doesnt throw any errors so i didnt think about it much
ima change some stuff around and test that
got it working ^^
it was in fact the references missing due to how they were set i assume, im surprised that made no errors and worked fine in the editor
Thank you !
Another little question, are local (self) network events instant or is there delay compared to directly calling a method ?
they are instant, and also ignore the network event queue
nice nice thank you :3
and not just if you target Self, it's for any event type that would end up targeting self
(like when All sends to self, or if Owner is also the local player)
with network events the latency is pretty much just the latency between the players, so it being bad depends on what you're doing or the players involved
if you're awaiting a player to shoot a gun, that would feel bad
if you're awaiting a player to place a block on a grid, it might feel fine
i wouldn't really frame it as preventing cheating though, in the worst case the host could be the one cheating. it's more like central actions are something you can use if it makes more sense logically
Yea, I had that in mind
The whole hacker just being the host to send out host truths
Something I'd be fine with
Prevent nott erm
Eliminate sorta deal
Though, I have to wonder if it's even worth the implementation at that rate even still
Feel like atp its gg.
Yeah I guess it's the difference between all lobbies being compromised or when they manage to get game host
But ugh
Maybe they can just give it to themselves or something
p2p makes my blood boil
vrc isnt p2p tho
It is. It only uses a server as a go between for the info for security and such, but doesn't do anything with that info. Just passes it along to the other user.
i'm pretty sure this callback is enforced relay side. if you always return false on this, only the master would ever be able to own or serialize the object, even if someone tried to set ownership (they could still appear to own it locally, but not remotely)
https://creators.vrchat.com/worlds/udon/networking/ownership/#onownershiprequest
I have my data fields set up like this:
[UdonSynced, FieldChangeCallback(nameof(ScoreData))]
private string[] _scoreData = new string[GameController.MAX_PLAYERS];
public string[] ScoreData
{
get => _scoreData;
set
{
_scoreData = value;
OnScoreUpdated();
}
}
I feel like this should work when later in the code I do something like this:
ScoreData = newScoreData;
However it seems like the FieldChangeCallback never fires. Is that because it's an array and it goes by reference or something?
Or is it something like Udon comparing and updating the ScoreData array by index and only assigning into the individual entries of the array, never actually reassigning the entire object?
yeah it being an array will affect it.
FieldChangeCallback only works if the value of the variable changes to a new one; but in the case of an array, this does not include the array contents. Since, if you know about arrays, the actual "value" of the array is the pointer to the array's contents, not the contents themselves
so for FieldChangeCallback to fire for an array, the entire array would need to be reassigned
Right, but I am in fact reassigning the array on the object owner
ScoreData = newScoreData;
I was wondering if Udon on the network recipients is not actually reassinging the array if it figures out that they are the same length
and it only diffs the elements or something
it should still be seeing it as a different array, since the pointer will have changed
On the data owner it does
(this is usually why, for arrays, I wouldn't use FieldChangeCallback)
It just doesn't work right I assume?
I'd assume it's like what I presumed, which is that Udon does not automatically create a new instance for the array assign if it can avoid it
does the serialization actually succeed when you sync it? Or is it OnScoreUpdated that you're not seeing fire?
No, the property setter is not being called
hm... how you have it should work
that's my hunch yeah
yeah, it makes FieldChangeCallback just not behave properly with arrays at all
What would be the best alternative then?
You could just call OnScoreUpdated in OnDeserialization instead
I've looked at that function before, but within it I can't really tell which field got updated, right?
so if I've got multiple fields that get synced, they would all need to have the same handler code
unless you're like incrementing something locally when the score changes there's not much difference calling your OnSomethingUpdated methods in series
I have omitted my client prediction code from what I've shown above
wouldn't be too bad to update all of it, but it'd be cleaner not to
So looks like OnDeserialization is best practice then for arrays?
i'd consider OnDeserialization best practice in any case personally, because you can read stale values if you ever try to access values other than the one being changed in fieldchangecallbacks
Yeah that's true
yeah FieldChangeCallback is best for a value that's handling some kind of state, and doesn't depend on any other networked variables
I was more thinking about reducing computing cost as much as possible, but I guess it's not too bad
that's what I'm doing yeah, it's just that this state happens to be represented in an array 😅
either way, I'll be doing it with OnDeserialization, thanks for the help you two :)
I guess one way around it is to serialize the array data into a string and send that over the network and then deserialize it in the callback
I know I've long stopped using FieldChangeCallback because it personally never really made sense to me to use
if you really want to mimic the fieldchange, you could cache the latest. check the cached array length, then elements, against the new array and only fire the change method if it differs at any point
though i wouldn't personally structure it around that, i think it's more clear/clean to simply reinterpret the absolute state of the networked variables at any point they come in
this is the closest thing you’ll get, yep! I’ve done it before and it worked great
my latest solution is to send an arrayUpdated event that makes other players run a delayed event using Time.realTimeSinceStartup - SimulationTime(NetworkCalling.CallingPlayer) as the time offset, so you wait for their data to finish serializing
the reason I chose this way this time is because not all players need to run the arrayUpdated, so they can check in the networked event first 👌
Is there a way to send a custom event to a specific player ? should i just send an event to all with the target playerapi as parameter and each recipient checks if its them on their own ? or is there a proper way to do this ?
unless you are targeting NetworkEventTarget.Owner yes that is pretty much the only way
oki thanks ^^
Can a joining player receive custom events as soon as OnPlayerJoined triggered for others ? or can there be a delay before they can receive custom events ?
looks like it works
nice
when sending an empty string over the network, or an array of empty strings, will udon convert it to a null string? I'm getting a weird issue where i send precisely empty text, but when I am checking the text on the receiver end I am getting (mystring != "") returning true instead of false like i would expect
Might make sense to Debug.Log the typeof(mystring) and the value
If you are directly comparing the Arrays as well instead of the elements, quite a few languages will always return false unless they are the same reference
im comparing array elements, dont worry
use string.isnullorempty instead of != ""
this was the solution i ended up going with. probably better practice to always use this, just he first time i ever needed to 😅
never heard of that until now, that’s some good knowledge right there
Is it best practice to mark all my UdonSharpBehaviors that don't have any UdonSynced variables with [UdonBehaviourSyncMode(BehaviourSyncMode.None)]?
I'm not sure if it would create network ids for GameObjects with those behaviors even if they don't store any network synced state, or if that would affect network performance.
i consider it best practice to explicitly mark them, and default to None unless i expect to need something on them. Manual if i'm using manual or need network events on them. i'd specifically advise against NoVariableSync personally
there's likely some (minimal) overhead for synced objects to just "exist" in terms of vrc's relevance/grouping of synced objects
Gotchu, thanks. My instinct was to mark them with None yeah. I think I saw somewhere that NoVariableSync shouldn't really be used? I don't really know what it is even for.
on paper i think it'd be to only allow network events on it, under the hood i'm pretty sure it uses continuous and may interact oddly with other sync modes, i consider it deprecated/legacy myself
Hewwo ^^'
is it possible to SendCustomNetworkEvent with arrays as parameters ? (int[] and byte{]) ?
in the manual, it says that UdonSync types are supported and at the bottom of the type page there is a mention for arrays
am doing some testing
yes, this is available, as long as the arrays are not too large
The total size of all parameters in a single network event cannot exceed 16 KB.
I just tested it and yeah it works, I'm just putting BS in the arrays ^^'
Thanks for the tip though
having weird issues with an NPC that I have walk back to its start point at the end of game. The NPC is VRC Object Sync'ed. Position is sync'ed fine. When there's only one player in the instance
transform.Rotate(0, 180, 0);
works just fine. If a second player joins, my NPC moonwalks. Not sure if the Rotate is firing n times, or not at all.
I don't bother with setting ownership on the NPC since no player directly interacts with it in any way. The rotation is sync'ed (we both see the same thing), it just doesn't seem to trigger with multiple people (or it's happening multiple times - not sure yet)
Is using gameObject.transform.Rotate just bad practice for Udon networking?
Do I have to do something like
SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TurnAround));
public void TurnAround()
{
transform.Rotate(0, 180, 0);
}```
Moonwalks for who? Who is the owner?
Everyone in the instance. Instance owner would own the object
That would be the problem, instance owner had nothing to do with networking object ownerships
Since both clients thinks they are owner and rotate it and then the real owner syncs it and it rotates again and it is just constantly having schizo moments about what it should do... you should do a check before the rotation to check if local player is owner
If not owner, dont touch it, it aint yours
There should be only one owner per object, you can transfer it but that is beyond your situation
okay, so I'll add a Networking.IsOwner check and play around with that. Thanks for the insight!
as a suggestion, Id say stay away from object sync on npcs and instead give networked destination goals or tracking targets and let all the calculations of movement happen clientside, specifically with nav agents. Only have the owner calculate out a target/destination and send that to everyone. Its much lighter on the network and gives you way more control and scalability.
that would be great except almost all of my npc logic runs on the owner, without object sync the position desyncs
it works fine if it's only one in the world but with many of them it would be a problem yeah
Hewwo,
I'm doodling with players atm and I have a question :
When a player joins, OnPlayerJoined is called
There is also OnPlayerRestored whne the player's data are loaded and ready.
If I want both the player to be joined and it's data to be ready, what should I use ?
I assume OnPlayerRestored is my man ?
if it's persistence data you're waiting for, then yes you want OnPlayerRestored
If NPC positioning is deterministic, you can only sync something like a server time value determined by the owner and have clients just run their NPC simulation. You could possibly periodically send out a sync update from the owner to ensure all clients are in still in sync.
Only sending out updates when data for the NPC changes would reduce network data a lot.
yeah I could probably try that approach but it definitely takes more work than just slapping object sync. also I remember running into that moonwalking issue as well
ok i have an elevator animation for a map and it auto moves up and down but its dsynced anyone know how to make it use the first joins animation time or something?
there's a number of ways to go about it, but what I would do is have the game master sync some variables whenever a player joins. These should be the server time and whatever you use to sync the position of the elevator. Then on deserialization, you can use CalculateServerDeltaTime() to get the difference between the current server time and the server time that was synced in order to accurately set the position of the elevator.
https://udonsharp.docs.vrchat.com/vrchat-api/
https://creators.vrchat.com/worlds/udon/networking/
Multiplayer experiences are at the heart of VRChat. To create a world that reacts to players and synchronizes data between them, you need to understand networking in VRChat.
I haven't really done much networking myself, so I'll probably not be great help with this, but from what I've seen in the research I have done, this would be the correct way to go about it
how would i do that tho for animations using animatiors? i know how to for phyiscal objects like pickup but idk
I believe you can get the time with Animator.GetCurrentAnimatorStateInfo ? I would look at the unity manual online for more info.
@tropic cedar main question is how long is that animation
25sec
huh not great not terrible. easiest way to go round this would be just syncing animator param tho youd need more than just one state animation
ie, elevator is down, elevator is moving up, elevator is up, elevator is movin down each of them being its own state with its own parameter
uh try this then ig https://github.com/Pokeyi/VRC-Animation-Sync
ok
most pcs do have a properly synced time anyway
otherwise itd be an owner storing animation start time timestamp and then setting animation to some percentage of that locally. doable ofc but not entry level
The SDK video player example prefab uses essentially the same basic principle, maybe it can adapt to a animator
I was confused here, on https://creators.vrchat.com/worlds/udon/networking/
In bubble gum example it got no attributes for "Trigger" event that being called to the network for all users and yet in "Network Events" page it says the method must use [NetworkCallable]
currently I'm getting "network event ... does not exist on target udon behaviour ..." which I fixed by adding [NetworkCallable] to that event
tldr issue is fixed but docs got me confused for a moment
Multiplayer experiences are at the heart of VRChat. To create a world that reacts to players and synchronizes data between them, you need to understand networking in VRChat.
that uses legacy network events, which don't need the attribute; network events can be called on any public method without an underscore prefix. they can't pass data unless they use the attribute version
If it wasn't working for you, maybe you didn't make the Trigger function public?
oh, yes it has an integer in the method, I guess that was the reason it had to use NetworkCallable attribute
Yeah that would do it
How do we deal with race conditions with vrc object pool spawn at specific locations over the network? Network events are randomly firing before the vrc spawn is firing unless I put a huge delay on the event and that never guarantees network latency conditions so it might just fail anyways if you are far from the host IRL. Setting synced variables directly doesn't seem to want to work. I set the variable, and then do an on change it wont fire, I'm guessing it's because the object is disabled by the pool and this is happening before the object is spawned.
Am I expected to build some big queue system for spawns and queue up vector3 positions and push/pull them when the object is confirmed available because that just seems insane.
IIRC think in backrooms corrupted I had like a holding area for the objects/enemies and with huge mutli second delays to let everything settle but there has got to be a better way.
@jolly star at that point stop using vrc pool, just disable stuff on object. idle vrc sync doesnt produce any network load
You mean use object sync but then disable it after the object is settled for a late joiner?
i mean for pickup make it invisible+no collider+kinematic, then its as good as nonexistent for a player without disabling an object with networkin on it.
vrc pool is an edge case where its okay to disable networked objects, until it breaks cause you need to apply smth to it, not just spawn, then it is easier to avoid it
I suppose you could do everything manually but that seems like a lot of extra work to manage lists of objects and their sync states for late joiners and in-game networked players versus just having a holding zone with a ~1-2 second delay on spawn before being allowed to manipulate things.
I suppose my frustration is they have this feature to save a lot of effort but it is and has been very obviously broken and could be hot fixed by just adding a parameter for tryspawn to take vector3/quat for a spawn location.
Yah generally speaking you want VRC networked objects to always be enabled and instead disable the renderer/collider/etc components to "disable" it
the VRC Object Pool is only really designed for very simple use cases
actually tried and it works quite well, didnt take that much effort
Is there any better way to sync fast moving rigidbodies? I have a basketball in my world using vrc object sync, but for remote players, when it bounces on the ground, it typically ends up appearing to float, like it bounces about a foot or two off of the ground
Networked ball physics
The bane of game devs everywhere
Is there any room for determinism or states in your project? Or do you want the players to physically control the ball all the time?
It's the difference between a "dribbling state" which can be easier to sync vs a full on physics sim over the network
Can I use VRCPlayerApi.SimulationTime to check who the player who has been in the world the longest is? If no, is there anyway I can check how long a player has been in an instance without leaving?
I could do something myself but I would really rather not
the person who's been in the world longest will have the lowest player id
typically they'd also be the master
is this true regardless of how old the instance is or who joins and leaves? i.e. if the master leaves, will the next-oldest player then have the lowest player id?
player ids aren't recycled and are given out in order of joins, so the eldest being the lowest should always be true
whether that person is always the master is questionable because vrc may prioritize more stable players to be given master at the point of the master leaving; the master can also go unresponsive for long enough that master is transferred, but i'm not 100% sure what the rules are right now; vrc has changed them a few times
I don't think my use case will care if they are the master, so this works perfectly, thank you 😄
"You must not rely on any particular player becoming master. The new master player will be chosen based on various criteria on the server side (platform, network conditions, etc.)." - https://creators.vrchat.com/worlds/udon/networking/ownership/#the-instance-master
Introduction
Hewwo
I have void OnPlayerRestored(VRCPlayerApi player) being called twice for the same player, is it normal ?
I know it is called once for every player joining but I didn't know it'd could do that multiple time for one player ?
Am double checking atm
my understanding is it shouldnt
is it in quick succession or after a while? what happens with the data after the first and second?
in quick succession
I get
player 1 restored
player 1 restored
player 2 restored
it is the the multi client vrc test though ? so could be why
maybe
not sure why that's happening, but depending on what you're doing you could use OnPlayerDataUpdated instead; this event gives you info if the data was actually changed or not, so you can use that to check if you actually need to update anything with the data
Do we have a way in udon sharp to detect specifically the start and end of a click interaction with a trigger? I.e. OnInteractClick and OnInteractRelease
why do you need these events? I don’t think they exist like that 🤔
From memory there is, on use down and on use up, something like that i cannot check right now
That would be for the player's controller inputs
I don't believe you can get a "release" event for an interaction -- it's an instant thing
However, you can note when an interaction happens, then watch for the corresponding input event
basically just want to do a click and hold
I suppose I could coopt interact to start a timer and then use that in addition to onusedown and onuseup
Does anyone know when the "VRChat Udon - What is networking" video says "join world -> get latest data -> onDeserialization event fires for them for every networked object"
Does the "get latest data" part mean latest variable values?
yes-ish
Yes, OnDeserialization fires for every networked object after it received updates to its synced fields.
When a player joins a world, they receive the current state for all networked objects automatically.
By "trigger" do you mean a Unity Button? Or do you mean Udon's "Interact" functionality?
“Current state” = current synced variable values. Is what I am getting from this then.
Yeah, the "current state" is the values for each field on the object owner's end.
Thank you very much for the information, I really appreciate it!
An event
I ended up figuring out a pretty consistent solution using a combination of Interact() and InputUse events
Just needed a "hold for x seconds to trigger interact" thing
(this does have Udon networking was the start of the problem also I don't know where to put this) I'm having a big problem and its not even my fault. I did 4 instances by mistake. I was testing the networking. Now every time I enter my world it says unusual account behavior. I changed the networking Ids and now I get the offline problem. And still the unusual account behavior every time I try to test build my world or enter into it playing vrc. The unusual account behavior started when I was doing networking with Udon sliders then it just happened.
Yeah that sounds like the problem
I have even made a new world file. Still does the "Unusual client behavior"
try delete the slider with the udon
Is there any concern about the number of network IDs you have in a world? I've ended up with about 400 or so, and if there is a concern with the number, what's the best practice for keeping the count low?
i consider there is likely some overhead for network object count, but if you're using pooling it might be necessary (i'd consider 400 okay personally, but it depends what you're doing)
if you can bundle multiple objects together logically that can help. imagine i have 9 bricks representing a wall that players can damage, each segment of the wall can be destroyed
instead of holding 9 synced objects (1 per brick), i could hold 1 synced object per n bricks, syncing their states on an array, which remote players can apply any time states come in
in my example you'd want to balance how many objects you bundle, to make it both advantageous for sync count while keeping it low enough that it can be synced as often as needed
I am using pooling but it's local pooling; I don't have any synced objects, and only 1 script per prefab needs to do any networking. All my networking is also done via events, i dont do manual or continuous sync at all
One thing that struck me as odd is that even though I have my other scripts marked as [UdonBehaviourSyncMode(BehaviourSyncMode.None)] it doesn't seem to stop it from creating a network id for the script
are they just pickups contributing to network ids?
i guess
i wouldn't worry about it then
nice
Ok I was hoping that was the case. So the actual count isn't that important, more what happens with those network ids?
they're just numbers used to refer to the object, i don't know if they iterate them periodically if they lack udonsync behaviour
i would hope that yeah, if it doesn't have an udonsync behaviour that it wouldn't contribute to overhead
cool cool, thanks for the help as always occala.
I have the same sliders in my test world no problem
Is 400 the limit? I have 380. I didn't know theirs like a limit to networking ID nothing told me
no that's just how many i happen to have
I noticed my network id count was scaling as I add more copies of the prefab im working on developing, and just wanted to head off any possible issues before i ran into them
My brain is going to explode from my brain-dead problem
this is why i don't trust anything given to me
make it from scratch u know it works 👍
With world building in vrc you never know what will work. It always wants to break. I have had the same udon code break in 1 world because of stations.
Yeah it's one of the reasons I decided to do everything from scratch
if something breaks I know it's something i wrote, and therefore something i can fix
i just dont like having blackboxes in my code
well 400 is def not smth to be concerned about, as long as theyre not continuous or moving pickups (vrc sync)
try regen network id
If you're looking at the id count in editor, I'm pretty sure every single GameObject with a UdonBehaviour on it gets assigned a network id, even if it has networking disabled. This is likely the best way to handling things due to UdonBehaviour's having the ability to have their networking behavior changed at any moment.
I'm not sure, but I assume that when you're building/uploading your world, all these excess network ids are automatically purged. Or maybe they don't and it doesn't even have a meaningful difference.
So, when a late joiner joins the world and gets the latest variable changes, they also fire fieldchangecallback and the methods they trigger if the values of said variables were different from default?
yep!
I use fieldchangecallback for all my late join behavior
It has been a journey the past few months. Slowly but surely getting the hang of this!
Make sure if you sync arrays this way that you don't init them with the same length that you expect to get synced or the callback won't fire
wha, the callback only fires if the array length changes and not its contents?
well, it only fires when the field gets set. Udon diffs the array elements when the array is the same length instead of creating a new array object and is therefore not setting the actual field.
yessss welcome to a dumb part of udon smh 😤
really just don't use fieldchangecallback for arrays. Use OnDeserialization
use OnDeserialization for all networked value, imo
and deny myself the convenience of it? no way
As far as I can tell, fieldchangecallback doesn't really make sense when it comes to how networked values work and trying to use it with networked values can produce race conditions
it only produces race conditions if you make a FieldChangeCallback dependent on another networked value
but it's very useful if you just have one variable that's meant to track a state, and you always want to keep the changes in sync with the value of the variable. In this case, not only does it avoid bloating OnDeserialization with stuff, it's an easy place to keep things in sync from the Owner vs. other players
it's a place where you can update the state for both the Owner and other players in the same place, rather than needing to juggling something like an "ApplyState" function that you call in both OnDeserialization and in OnPostSerialization, for example
Could you explain what you mean by "bloating" OnDeserialization with stuff?
sometimes I use it for non-networked things too. Say I want the color of one thing to be blue or red depending on a condition.... instead of just setting the color on the material manually in every instance that state changes, or making a whole new function like "UpdateVariable".... just do it in FieldChangeCallback. Then I just set that variable whenever the state needs changing, and FieldChangeCallback automatically handles everything it needs to do then, without bloating up that portion of the code with unnecessary things
Don't you need to make a property for FieldChangeCallback to work? Can't you already get what you want from simply doing it via the property and cutting out the extra FieldChangeCallback stuff?
Without FieldChangeCallback, then anything I'd want to do in there, I'd have to do in OnDeserialization instead. (which again, doesn't run for the sending player).
So instead of having things organized under a variable's FieldChangeCallback depending on what that variable is meant for, it would need to be done under OnDeserialization, and then I'd also have to write extra stuff that tends to disorganize things to make sure the same things happen for the Owner and receiving players....
I dunno. I think FieldChangeCallback just allows you to remove a lot of the networking headaches that can potentially happen.
Obviously not useful for everything networked related, as you've noted, there are limitations to it
I don't know, maybe? Certainly in Graph you would need the equivalent of it
it's really the difference of "can I do a thing when only this variable changes" or "do I need to wait for ALL network variables are confirmed ready to use before doing a thing"
But you're already writing that same code elsewhere (except with all the syntax to support FieldChangeCallback) so you're really just moving the "bloat" elsewhere. The main thing you are accomplishing is obfuscating the connection between OnDeserialization and the code that handles the changes in those networked values. And that obfuscation is what allows various issues, like race conditions between different networked values and arrays not working as expected, to appear in your code.
You're, of course, allowed to obfuscate that connection if you want, but I'd personally HIGHLY disadvise it for anything but the most simple of networking.
I don't see it as obfuscating.... I see it as more as organizing
like FieldChangeCallback is a little folder for just that variable. It's the things I want to happen only when that variable changes
It's objectively obfuscation, is it not?
I don't know if I'm getting this right but it sounds like from your perspective, "OnDeserialization" is "the place where networked stuff happens"
but it's not the only place
so, I wouldn't consider it obfuscation
arrays absolutely don't work as expected in FieldChangeCallback. That's definitely clear, and not something I'd use when syncing arrays
You're obfuscating the connection between the externally fired event that changes your networked values and the internal code that handles changes in your networked values
...no? I really don't see it. FieldChangeCallback can also be a place that handles changes to networked values.
Yes but FieldChangeCallback is not explicitly a network connected event which is where the obfuscation comes in
I guess?. I think. It's used for both
As I was saying, it's code I want to run whether or not the variable changed locally, or it was changed via the network. Rather than build some dedicated functions for that, FieldChangeCallback handles both simultaneously
"it's code I want to run whether or not the variable changed locally, or it was changed via the network. Rather than build some dedicated functions for that"
That is what I meant by obfuscation btw
alright, but.... I'm failing to see how that is a negative thing
Because it hides, obfuscates, the precise way that networked values are handled. It lets you avoid writing specific networking code, the benefit of obfuscation, but it hides the precise way that network code works and thus allows errors with race conditions and arrays to appear.
It seems more practical to fully acknowledge and respect that networking requires its own coding style and approach and to fully commit to that, rather then trying to piecemeal it.
I can’t understand a word of this 😂
I don't really fully get it other. Or moreso still agree with it. I don't see it as hiding it at all..... the FieldChangeCallback IS my networking code. it's not pretending not to be.
but it's not the only tool in the toolbox.... all depends on what I need to have my code do
Semi-related, it's possible for OnDeserialize to also not receive fully consistent data, right?
If I change two fields and call RequestSerialization(), it's still possible to split it into two frames
if you're getting race conditions, thats not the fault of FieldChangeCallback, that's the fault of not understanding the execution order of when FieldChangeCallback runs vs. OnDeserialization
you won't get the inbetween state if RequestSerialization is called very quickly, it'll only send the latest serialization at the next network tick
Perhaps a better choice of work here would have been excessive abstraction; it abstracts out the connection between networking and value changes to a degree that allows issues to appear
Well I'm saying something like this:
myField1 = newValue1;
myField2 = newValue2;
RequestSerialization();
Is it possible for OnDeserialization to be called twice on a client if they receive both changes in separate network packages?
no, in this case they would be sent together
is that ALWAYS the case?
yes
no matter how much data?
all of the networked variables on a single script are sent all at once during a serialization
on a single object* rather
per network id I presume
right ✅
I gotchu
by "how much data" if you for example sent too much data in one serialization, the serialization would just fail and not send at all
it won't break it up and send it in parts or anything
I'm sure there's a reference for this, but do you know what the max size of a network frame is, just out of curiousity?
of the top of my head I think its 4 times every second?
oh not network tick your asking data size
ya
yea it's in a doc
depending on how pedantic we want to be we could say the data is sent and recoieved in parts; but in any case I'm pretty sure OnDeserilisation is only called once a complete individual state (which includes all networked values on that object) is received
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
that one
turns out, a quite substantial amount
that's it ^
yeah manual lets you do A LOT. but sending a package near the max size takes about ~30 seconds
that would be the most sane assumption, hope that it just calls the callbacks one by one as it assigns data, then calls OnDeserialization once after
to be clear I've not even come close to being anywhere near this limit, I just wanted to know 😂
right, like I mentioned before:
FieldChangeCallback: only this variable is ready with its new value. nothing else is guaranteed
OnDeserializzation: ALL variables are confirmed to have their latest values and are ready to be used
I have no idea how it handles FieldChangeCallback/the timing of it; that part of VRC's code might be a blackbox and at the very least could be changed at any time and shouldn't be depended upon
I think the order of individual variables' FieldChangeCallback order is not guaranteed, but they all run before OnDeserialization
If you actually have stuff that's important for data consistency, use OnDeserialization
An example of when I'm using FieldChangeCallback is when it's non-critical UI display data that gets synced from an object owner.
Instead of having to update large parts of UI with every small change, FieldChangeCallback lets me only update small parts as they are changed.
because if you think about it. OnDeserialization is the confirmation that the new values are ready. Well, the only way they could be ready is if the variables have their new values right? And what runs when a variable gets a new value? FieldChangeCallback. thus, they all run first
most of the time if a field change variable depends on another in the same frame you have to toss the resulting code in a one frame delay, in my experience
which is a hacky way to jump around the race condition, but for something like that, then it might be better to have that logic in OnDeserialization instead
this kinda thinking stems from web dev frameworks such as Vue I guess, guh
The way I tend to handled my networking stuff is that anything directly connected to the networking is explicitly and rigidly handled state based logic; and then everything that derives from the network data can be more flexible and dynamic
you are describing exactly what I would use FieldChangeCallback for 😁
FieldChangeCallback does not support explicitly and rigidly handled state based logic; it handles data individually rather than in the monolithic way that state based would imply
Which is perfectly fine if your state is only determined by that one variable
Sure, but then you're handling the same goal in different ways depending on how many networked variables you have when you could just normalize the process
i consider fieldchangecallback to be a bit of a trap, like it's fine for a few unrelated vars, but it breaks down if you use arrays or have any related variables or enough variables. i wouldn't really say it's wrong to use in pretty constrained cases, but i think it scales poorly and can be more error-prone if you add things in the future
from a maintenance standpoint it's less clear defining the behaviour across variables, defining it at variable-level to begin with is less readable imo. i guess you could call into methods for each var, but at that point i'd consider it ideal to just call it in deserialize
using deserialize to centralize is much more clear and contained, it's more legible in terms of execution flow, instead of peeking every variable if you need to see what's being networked in your code
// The owner calls this instead of RequestSerialization()
private void CallSerialization()
{
RequestSerialization();
ApplySerialization();
}
public override void OnDeserialization()
{
ApplySerialization();
}
private void ApplySerialization()
{
ApplySomeColor();
DoSomeOtherThing();
MaybeAnotherThing();
SomethingNotAssociatedWithAnySpecificVariable();
}
I just don't think I'll ever use it tbh
Always felt funky to me in the same way as one who uses public members felt funky
Might provide what's needed
But could also more easily cause a slew of problems
I like the UI usage though
You guys still aren't making any sense :/
I see no difference in having an ApplySerialization method, versus a FieldChangeCallback, besides execution order.
Again I want to emphasize it's extremely useful when you want something to happen when THAT variable's value changes, regardless if it's a local change or has happened over the network.. Any time I see someone run into issues using it, it's due to assuming that it is purely a networking-related feature, and all variables are safe to use. And as I've explained, this is not true.
it's fine for a few unrelated vars, but it breaks down if you use arrays or have any related variables or enough variables. i wouldn't really say it's wrong to use in pretty constrained cases, but i think it scales poorly and can be more error-prone if you add things in the future
i'm advising against it for scaling, i'd admit that it's fine for cases where the variables are legitimately self-contained, but can, by design, lead to errors if you're not slightly careful with the scope
i personally find it less clear to optionally apply state only on change, preferring to apply absolute state every time, unless it legitimately impacts performance, that's just my preference though
i'm probably too critical of it, but i think it's a legitimate take on the boundary
you're right, if it's scoped to just that var it's fine
like if someone was showing me some learning code for udon and they had a single synced var with a fieldchange i don't think i'd even bring it up, but if they introduced 3 more vars and started showing a pattern of applying state across all the vars separately, i'd probably try to inform them of the caveats and preference to use deserialization
because it could develop into a pretty poor pattern or outright bugs if they never understood the distinction, though i'm not saying that's the case here
Now you're making sense, that's basically everything I've been trying to say
Just feel like this discussion has boiled down to
"Don't use thing. It causes problems in X scenario."
"Actually, thing is quite useful in Y scenario."
"No, don't ever use thing."
i think my point would be that by nature, applying in deserialization means there's no room for error. for someone completely aware of the potential issues i'm sure they could manage it themselves, though i don't like managing that distinction
for example, i click a button and display my high score on some synced board
VERSION 1
public class SomeClass : UdonSharpBehaviour
{
public TMP.Ugui scoreboard;
[UdonSynced, FieldChangeCallback(nameof(PlayerHighestScore))]
private string _playerHighestScore;
public string PlayerHighestScore
{
set
{
_playerHighestScore = value;
scoreboard.text = $"{value}";
}
get => _playerHighestScore;
}
public override void Interact()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
PlayerHighestScore = GameManager.GetMyPlayerHighestScore();
RequestSerialization();
}
}
then i realize i actually want to display the player's name too, prior to the score, so i make this
VERSION 2
public class SomeClass : UdonSharpBehaviour
{
public TMP.Ugui scoreboard;
[UdonSynced, FieldChangeCallback(nameof(PlayerHighestScore))]
private string _playerHighestScore;
public string PlayerHighestScore
{
set
{
_playerHighestScore = value;
scoreboard.text = $"[{PlayerName}] {value}";
}
get => _playerHighestScore;
}
[UdonSynced]
private string PlayerName;
public override void Interact()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
PlayerHighestScore = GameManager.GetMyPlayerHighestScore();
PlayerName = Networking.LocalPlayer.displayName;
RequestSerialization();
}
}
you could definitely make the argument for just making it one string. or inferring the player name from ownership. or applying it in both. there are issues with doing it this way obviously, though it may not be apparent at a glance to someone unfamiliar. but i don't think i'd want to differentiate between "this is the point where i move my logic from a change callback to deserialize instead", i'd just use deserialize to begin with
mostly, i'd want to put across that this is simply never an issue if deserialize is the point of call
I’ve never used deserialization for networking because there are places that callbacks support more, such as being able to interact with the original value on change before applying the new one over the variable
also nobody has ever explained how to detect changes to specific variables in deserialization, and I don’t see any reason to trigger an update to every single synced variable on a script just because I received an update for one or two variables
seems more self contained to me to put them in a callback so their behavior is handled as needed, specifically only when they’re changed
you receive every value whether they've changed or not, i think deserialize reflects that more logically
i do a lot of networking and i've never had a case where applying the entire state was the breaking point between deciding to apply it all or only some, in the worst case you're applying everything if it all changes. it might sound like a gain, but in practice unless you've measured that it's causing you issues it's really not worth selectively applying
as for how you'd detect that, if you were updating a text field, you could check if the synced string is equivalent to the text on some field you plan to update prior to pushing it, but again i really think that's not worthwhile unless you're doing it for a reason
it honestly sounds worse performance to build the system to apply extra code changes without detecting any reason to do so :| especially if you want something to only happen when specific variables are changed, since you can’t detect a changed state without separately cloning every variable into a local state and comparing those for each variable in deserialization
also think storing all the networking code for many different variables in one place would get way more cluttered
the things you're doing in the logic matter far more than deciding to apply it on fieldchange or on deserialize, instantiating 1500 objects will be an issue in either case. the performance gain is trivial compared to the logic in most cases
like a bundle of variables, a snapshot, is something that at worst needs to be interpreted wholesale, not piecemeal (a late-joiner is the prime example of this)
being able to infer the absolute state at any point across all variables aligns more with how it actually works
but fieldchange is fine if your variables have zero correlation to each other ever
Just tested that RequestSerialization will trigger a network sync on the owner and a OnDeserialization on remote clients, even if there are zero changes in the networked values.
This is actually desirable and helps me with a Two Generals' Problem I had with some networking I'm working on.
Consider a block that has it's type denotated by an int; this value is currently set to 1 to denote a block type of one.
I want anyone to be able to change the block type of the block but I want to avoid ownership-related race conditions. As a result I'll have non-owners use a network event to request the owner of the block to change the value for them.
But what if the Owner crashes/dcs after sending them the request event but before they had time to change the block type? To address this I'd have the remote client remember that it sent out this event and resend it to any new owners if the old owner dced without changing the block type.
But what about the edge case of the block, due to some other actor, getting switched back to the default value of 1? The remote client would never see the expected value change happen even though their request event was successfully received. But since OnDeserialization we can simply rely on that to presume our event was received successfully. There is still an edge case that allows for an event to never be properly recieved, but I consider it small enough to be tolerable.
i'm advising against it for scaling, i'd admit that it's fine for cases where the variables are legitimately self-contained, but can, by design, lead to errors if you're not slightly careful with the scope
Marvelous
In a perfect nutshell what I was trying to convey here -> #udon-networking message
Systems that increase in variability to scale
I'd rather just use more reliable the catch all even for small things at that rate
Everytime i start a new project i feel like i need to relearn networking x)
you can detect it by having two variables.
One Private/Protected which act as a last known value.
One [UdonSynced] Public Variable that act as the newest value.
then in the OnDeserialize you can check the old last known value you have. And if it's different from the value you have that means it changed.
This will always ensure that you know its changed or not..
And then you can run whatever methods etc.
Or you can use FieldChangeCallback that allows you to see the old value and the new one before setting it. Do know that it does not run if its the same value.
does the edge-case happen to be if they see an unrelated deserialize come in just after they send to the owner?
Yep. The timing also requires the owner to be there to send out that unrelated network update but also gone in time to never receive and response to the new request.
have you considered the case of applying delta? (as something that's interesting to think through) ; imagine player 2 wants to deposit their persisted gold into a central box
- they must reasonably deduct the gold from themselves
- the gold must be processed at some point if they remain in the session
- the gold should not be applied twice
That sounds like a different networking goal than what I am thinking of. I'm setting a finite state, not accumulating an amount. Unless I misunderstand what you mean by delta and "depositing gold into a central box"
it is different yes, it was more of an open-ended question that i found interesting to think through after considering yours; i'm not really expecting an answer, just wanted to say that it's an interesting networking problem in a similar vein
Ahh I see
I think the way persistence data is designed makes it impossible to guarantee transfers like that
I'm not sure you could guarantee stuff like that without a central, authoritative source
the persistence part would never be guaranteed yeah, though if on the sender you deduct it first, serialize, then wait some timeframe, after both post serialize fires and clog is false, it's reasonable to assume that the persistence update will hit network
deducting first means they could lose the gold if it wasn't processed, but i think that's preferable to duplicating it; which leads into ensuring it's processed while they remain in the session
My idea would be to send the owner of the box a network event including the the amount and uid for the event; the owner would then send back a confirmation that the event was received including the uid for the event
then the deduction to the persistance amount happens for that uid
a uid (something like a unique id for that transaction) would be quite apt yes
the most important thing is, of course, that you don't have any network events that directly allow remote clients to control your persistence data
how is that a problem? you just have send the users ID, Amount, TimeStamp. that should avoid a problem? considering on the receiving end you can put them in a queue and add them based on the timestamp
unless i missunderstood the inherit problem
the case that guaranteed processing attempts to resolve is if the person who would process it centrally is unresponsive (suspended quest device, someone whose internet cut, someone in the process of disconnecting, etc.)
your event directed to the current owner won't necessarily be processed in that case
well maybe? but again im pretty sure a Queue should absolve it to some degree with some extra careful steps?
a queue on the receiver doesn't resolve if their client doesn't logically process it
well in case of vrchat its eventuelly going to happen no?
from the perspective of the relay, it sent the event to the correct person, even if they're currently unavailable, the event would not be resent to the new owner if the original disconnects, leaving it in limbo from the perspective of the sender
Hmm. i must say i have yet to observe that kind of a problem through.,.
it's a pretty narrow problem, it's not common either, but it's definitely possible if someone suspends their device or internet cuts out for some duration before they're forcibly disconnected
the stakes are more for transactions, which you can't secure in any way, but you can attempt to protect regular users from the issue i'm describing
in most cases the event dropping is fine if the user can just retry the action, in the case of currency it's not really a great experience for the player if they submit x currency and nothing happens
hmm true. but to avoid issues its eaiser to have one person be the handler? otherwise you would have to have CRDT
using a central handler is the norm in applying a delta like this yeah
the alternative is a race condition that could just as easily result in issues if you use ownership transfers and variable setting (i'd say more easily in the case of applying delta to a synced value)
yea i mean if you have an issue like you said here. you have 2 people updating and then lets say the person who is in charge of it have issues and it does not operate on the problem.
considering that eventuelly? it switches who the new owner is i hope.
but then again if we had a proper server/owner that never changes. it would make things so much eaiser but that like is more costly on vrchats side
Having it handled server-side would either require the data handling to be so limiting as to be frustrating to work with, or would require us to be able to write code that would run on the server which is its own whole thing.
it's not really more frustrating to work with at all. its the same way as you have been doing it now. all it would require on Vrc's side is to have similar thing like [UdonSynced]. and then have it handle it like so. Or if it only is for simple things like updating values then all you would need is to send it to the owner with the data obviously.
Cost may or may not be larger. But complexity on Vrc's side would be much larger. More to keep under control but also to ensure nothing bad can be ran on the server.
Also i would actually more or less call it instanced side owner. as in when an instance is being created it automaticly assigns the instance it self as a master and not the users.
What you described is already how it works. VRC's relay server already stores and distributes the synched values.
yea but we have no control over it. nor can it be the owner. since the relay just copies the data and sends it on
and relays cant really be managed either
How can we control the server-side data without writing server-side code and without VRC manually written (and thus so limiting to be frustrating if not impossible to work with) instructions?
you can't you have to write it as a server only running method. the limits would be the same as we have for the client it self and the only real difference is just what we would be allowed to run on the server.
So what, anyone in the instance would be allowed to drain anyone's persistence data inventory?
yo check out the new dev update, they're adding a tweening method to udon that seems like it would perfect for this
Persistent data can only be written by the player who own the data. It's just that world creator have some kind of network function that allow player to alter other player data.
I already know that. I was just pointing out the issues with what HostileLogOut's was requesting.
Are we talking about how vrchat should implement server-side script, or the way to implement server-side script in the current udon situation?
They seemed to have some amorphous idea that having "server owned" networked objects would solve common networking issues people run into
i don't know how you would come to that conclusion. Persistence data has nothing todo with the server.
eh cause it would. Having a single owner of an object for things like shared inventories, Banks etc whatever requires something shared between everyone. Would make it far eaiser as you only need to request to that specific owner. and you wouldn't need this to be replicated for every single user either. Or added or removed by every user either. And most problems that comes from multiple people trying to add/remove from a shared bank for instance would be gone.
Yes. all through it likely wont happen
Server Authoritative are far better then Client Authoritative for things like this.
Tenant users are complicated pattern by itself. Can't really see how vrc will support this. If the main point still is having server-side script, that's also better to have one.
Tenant Users?
A group of user that can access the same resource.
That's also accessing it.
well in a sense yes.
but you still have various checks to ensure that you cant just get the data that doesnt belong to you etc
like server sided scripts or functionality would solve a crap ton of issues that we have because its not a centralized Authoritative Server
and instead its all Client Authoritative
The main point is just to have server-side script.
yes the main point is server sided scripts would solve issues
there is this one https://feedback.vrchat.com/udon/p/server-authority-for-vrchat-instances?
the relay doesn't run a unity instance afaik, it wouldn't be able to do things like run physics or nav agents. at best it might be able to arbitrate some things centrally in primitive data is my impression (which isn't bad, but it's probably not a priority)
Helloo
Probably an easy question but how do i set the position of a synced pickup ? just setting the transform position doesnt work if you havent held the pickup last or if its being held, and i dont see a function to respawn it built in or a set owner either
How do i do this ?
U# btw
You are in the right direction, you need to set owner, only the owner has the "final say" of the synced variables (eg the position) so that is what you need to do before moving it
Go watch the pinned video, it explains the basics well
Yeah the OP was kinda crazy
Let's not act like server authority wouldn't sweep away a lot of common issues now
that's why i said if we instead of having a Relay and they would switch to the better solution of having a single truth for each instance. It would solve alot of problems aka each instance acts as the only owner of specific objects or all
huh? what OP and what are you trying to say?
Who did you reply to?
Might be the original poster tbh
oh eh. i honestly dont know LOL 😄 i forgot
Yeah, all that really matters is that we agree it would solve a lot
i'd love to have a proper client-server setup, but it'd be asking them to change their entire model. like it'd be a very extreme change on their end
it's kinda a proven fact. with how every single game that is networked uses it 😄
yea i know thats why i said the change would be costly.
(Sky might be blue)
but it would also greatly benefit us creators and them self in the end by reducing overall cost.
considering Vrchat uses the Legacy Proton Networking from what i undertand. and its the Relay Legacy
anyway it's a big ask and a big wish of mine we do. it would just make things so much eaiser for us.
For now let's hope for the absolute basics (how did we survive for this long without interfaces)
(VRC+ API before such features)
That was my point; that what you're imagining would require us to be able to write code that would run server-side, which is its own can of worms, and would not be as simple as this amorphous idea of "server owned network objects"
You can get around lack of interfaces by using SendCustomEvent (and optionally SetProgramVariable) and treating scripts as UdonBehaviours when polymorphism is required
It's a bit oh a hassle, but that's how people have been surviving in the meantime
Look at what they need for a fraction of our power sorta deal
What a mess
(we'll keep waiting for the absolute basics)
Hey, I'm pretty sure the people who created the foundation of the modern world did so without those fancy tools
I'm so glad we're in the current_era
I agree it's amazing that we just
Don't have to go back to that period
Punch cards for programming HAD to go
yes it's as simple as it sounds. and its not a amorphous idea or concept. It's just as simple to write as you are doing now. instead of thinking in terms of purely locally written code every bit of code written for the server would target x user. or all users. And Yes i have written server code many times before. with Darkrift networking, Unity Mirror etc from the very base
Vincill do you actually know what an interface is? and what it does. Cause you can't get similar or even close to the same functionality as Interfaces provide with anything Vrc currently has implemented. Since a Interface cannot be instantiated and can only contain abstract methods and properties.
(There are no contracts even if this wild polymorphism route remains)
alot of these things is what the promised Soba is gonna solve. If it ever comes out
Since you're confused, let me explain: the goal of interfaces is to allow different classes to be handled in a polymorphic way, allowing you to tell an object to do a specific thing even if you don't know what specific class it is. While you currently don't have the type safety and strict compiler-side enforcement of interfaces, you can still implement this core goal via SendCustomEvent and SetProgramVariable while treating the objects as UdonBehaviours
i think it's a stretch to say that we can achieve nothing close to an interface
a close approximation would be using a base type and deriving from it to guarantee the methods for a generic base
what vincil's describing isn't a bad approximation either if you enforce the methods are available yourself, but it's much more manual
Huh
All in all
We'll keep waiting for the absolute basics to be properly implemented
I wonder if what is considered "the absolute basics" will change once Soba comes out
Hopefully so
Iteration is awesomesauce
I'm just hoping we eventually get coroutines
im not confused lol.
we already have something similar. with SendCustomEvents etc and control them when they start/stop and how often it should run.
problem is you still need to instantiate the class in udon. and it still has to derieve from UdonSharp etc. Like the actual benefit of the interface would be lost in udon.
I already know, lol. I use them a lot in my code.
I read two crazy statements today
Someone proposing the concept that a base class is suitable for replacing interfaces (we desperately tried to escape inheritance hell and scope creep by creating interfaces in the first place)
And someone else talking about customevents as an approximation for coroutines
What a day
well the only reason for that is because of how we can do what exactly a corutine already does. just we can't get any data back from a CustomEvent etc. And a few other things that corutines can do which we can't but thats primarily making it wait until something returns
i said it was a close approximation, not a replacement. i think interfaces would benefit udon greatly, but saying we can't do anything remotely in that vein is an exaggeration
there isn't a ton of functional difference between providing IDamageable on a class and providing a base that can be damaged as if it were any type that could be damaged
if you want to say it's less composable i'd agree
I don't think I've yet to find a situation where what you can do with coroutines can't be replicated with the tools that Udon provides. Coroutines would however be a nice QoL, especially when wanting run simultaneous loops of the same method.
eh you can already run simultaneous loops of the same method through. Unless im confused by what u mean.
if you want the touch variables that persist across frames you need to create instance variable arrays and perform array management during the loop among potentially other loop management depending on what you're doing
Thank you!
Yeah, we (you AND I) know the horrors of such an approach, even if it's available it just doesn't mean much
I guess just to say it exists or smth
Absolutely
To be fair we both want what's best for the dev platform for this game
I just can't grant them laziness through workarounds or approximations or whhaatteeveerr else people keep talking about
Should just be implemented sorta deal
base classes can still be useful though if you actually want to make something. the alternatives are something like doing sequential getcomponent calls to find the specific type and call a method. or using generic string events/program variable sets. there are places where i describe a base type with base methods (where i'd probably prefer interfaces) and it works reasonably
i don't like inheritance, i don't nest more than one layer in my projects, but it's not like a base class is only a theoretically useful thing, you can use it in practice and it's preferable to just not doing the thing you want to do
They've created a platform where you can upload world that runs code that can can accomplish almost any goal you want all while requiring virtually zero verification.
I would never use the word "lazy" for what they've created, or what they need to do to expand Udon's/Soba's capabilities while retaining the freedom the platform allows.
I wouldn't call what they've created lazy either tbh
It is true that we have to use such workarounds as opposed to
Well
Sitting around doing nothing
In fact
We actually agree that it's not theoretical
I couldnt really say we'll wait for the absolute basics if this wasn't so
Even from the perspective of multi-inheritance, you cannot have a super of more than one class, but you can implement multiple interfaces in C#, so that's pretty good. But… I absolutely despise how you have to proxy properties with methods with interfaces and I've found getters/setters to be buggy. I was stuck in inheritance hell, but I was making a game with multiple gun types and they had to inherit from like a base class and different sub types like if the gun was to have recoil, was full/semi auto or single shot. This was done in combination with static classes that accepted their respected interface of type of weapon so I could de-dupe code and provide standard interfaces for future weapons
you can do composition instead in udon. it should provide a better solution. since each subtype should be its own class anyway
How much of their design philosophy is limited by security concerns, do we know? Im not a cs major so i dont have great background in these kinds of things, but if they dont implement something i want i tend to assume its more due to some possible risk of exploitation by programmers. Like allowing the io libaries is obviously a pretty catastrophic idea, are there things they just cant do because it would allow access beyond the sandbox they provide for us?
We (you AND I) can't really know
But we do know they use a VM so what the VM says goes
It shouldn't really be a concern from there
The implementation of the absolute basics would conform to what the VM can do when translated into yadda yadda bytecode
There are no surprises because it's top-down sorta deal
They made Da Rules
What about in cases where I want to extract a set of specific properties to a type which I will pass? I’d still have to make proxy methods and an interface it implements to access properties?
because interfaces cannot declare props directly
@sullen junco While Instantiate() is prohibited, is there way to spawn object dynamically?
Instantiate isn't prohibited, you can use it
Uh yeah I mean, it can't be used for network synced object
Then your only other options are either, use a PlayerObject, or have a separate, non-instantiated script manually handle syncing the instantiated objects
PlayerObjects would be easier, but only if it makes sense to use them for your use-case
There's also the option of using an ObjectPool
I would like to spawn a object almost infinitely and it should not belong to someone, so started to using Object Pool
Thanks :)
So I never realized that to do networking stuff you have to send the network event to a different udon behavior
So I been struggling to make something for over a week because of it
no? a behaviour can send a networked event to itself
It can? Nothing I ever made worked when I was networking to itself
yeah just SendCustomNetworkEvent. In Graph, just leave the "instance" input empty. You should even be able to select the event you want in the dropdown, if it's already set up
the only things I could see that would prevent this from working is:
-The script's sync type is set to None
-This script is on an instantiated object
If you know how to write a serializer, one way to give networking to instantiated objects is to create a pool of networked objects that sync a byte array and have instantiated objects that know how to serialize their networked data and deserialize it; then you have a way to link the networked objects to the instantiated objects in a way that doesn't produce desynchs
Is there anyway for me to test my networking without uploading the world
yes, Build & Test with multiple clients
Hey how would I make this server side?
Its a script for a button that changes the skybox but I can't get it to sync for players when one person touches it. ):
doesn't look like you have any networking there. I don't really work with Graph though
wait I fixed it
nice
forgot this little guy 😅
there it is
That might be wrong terminology, because there is not "server side" for Udon.
This isnt even an "um actually' thing so i dont get the reacts on rens message, networking in udon is designed as peer-to-peer, not server-client, its a really important point of understanding for designing your code and systems
Yea, there is no server, instead you have "owners" of each networked object, and ownership can be transferred and every object can have different owners, it is essential that you understand what an owner is
The pinned video is great, it is a must watch for anyone trying to get into udon networking, it explains this concept well
can confirm, there’s nothing server side in udon, bro’s just being rude for no reason after asking for help :v
I mean it depends on how pedantic we want to be. If we're speaking very technically, when we "RequestSerialization" on a manual sync object we are pushing the data to the server. The server is what then distributes that data to everyone else. And if I understand correctly that is how late sync is handled, anyone joining gets the last network data sent to the server and thus the owners of objects don't need to resync their data every time someone joins.
With that said "make this server side" still isn't good terminology here and can leave confusion on what is actually meant.
Oh how I wish we had a server
Somethingsomething relay
And voice channel "zones" to help reduce the bandwidth (I was told recently that lobbies get really bad fps the more players due to the connections you need to make to hear everyone)
Does OnPlayerJoined trigger for the joining player, using their VRCPlayerApi?
yes
it's also presented every player in the session when you join i think
including yourself yeah
NetworkEventTarget.Owner will target the owner of the gameobject the script is attached to?
yeah the owner of the object the network event is called on
nice, many thanks 😄
If I use Networking.SetOwner(playerapi, gameobject), i only need to fire it from a single client to apply to the whole lobby, right?
best practice would be to check playerapi.islocal before doing Networking.SetOwner so I don't fire the same request from every client, i imagine
in the context of a network event sent to everyone i suppose so
but yeah the setowner is automatically propagated, it only needs to be called from one client
perfect, thank you
current networking project: handle relatively high degrees of environmental simulation (think something like SS13) but on VRC's P2P networking
I think I can get it to work, just need to give a good amount of thought to the structuring
Always thought SS13 was so cool. I liked watching hour long videos of everything hitting the fan
Best of luck
even nicer would be something seamless. say, you're too far away from a player and cannot hear them anymore for some time, and if so, drop the connection. of course in edge cases where there's a large area and you'd want to keep the connection, defining a zone where it doesn't drop connections would be ideal
For the OnPlayerLeft event, will this trigger for the local player such that I can run code before they actually leave the instance? Or will this only trigger after they have actually left?
My syncing requires intelligent transfer of ownership and id like to avoid having to send out an extra ping to do so if i can
i might need to dedicate a variable to having a backup owner to handle this 🤔
@normal sundial you cant run code locally for leaving player. cause it can very well be player losing network connection, crashing or turning their quest off via button. so having this makes no sense. however nowadays player api is at least guaranteed to still be valid during that call for the others
alright, backup owner variable approach it is, thanks
If I send a string as a network parameter, is there a general character limit still to that? I read 50 but wondering if that's old info or just for a sync'd variable? It seems fine with going quite a bit above that but this is just in build and test so wondering if live with multiple players that would just fail.
I’ve synced strings with over a thousand characters
so I think the limit is just the sync byte limit
sweet, thank you. Haven't had an issue yet either so that's great.
out of curiosity I double checked, the size limit for a single network event is 16KB, and a string takes up 2 bytes per character, so that should mean a theoretical max of 8000 characters in a string I think?
this section of the docs has some additional useful info on this
https://creators.vrchat.com/worlds/udon/networking/events/#parameter-size-limits-and-event-splitting
Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.
I should be hitting about 1-2k so I should be pretty safe thankfully. I'm just time slicing to get more now :)
So far so good!
hello
less then that. the overhead + the string likely makes it to be safe around 7000 characters or so. also even then it will split every 1024 bytes.
I need to sync a small amount of data per-player. Would Player Objects be the way to go here?
They can certainly make doing that a lot easier compared to the alternative
in the past, i tried to create a dictionary that mapped player IDs to the data I was syncing
but i messed something up and never wound up finishing it
(this is also data that each player should own)
it's going to be used for toggles that other players need to know about
in this case, it's a toggle that prevents you from triggering a sound effect when you enter a collider
playerobjects are a little indirect but probably the right choice. you could use playerdata if you really felt like having less indirect access quickly, but it would persist the value (i suppose you could swap it false on restore)
PlayerObjects are the best way to ensure each person has an object only they can own. and you just need to have each person store whatever states u need to store.
you didn't watch the vid did you
source: become the owner - set the variables - request serialization - run custom event
other folks: on deserialization - same custom event
custom event: look at the variables and apply changes like open the door or set the material.
thats a flow for 99% of manual sync stuff
I thought you were sending me to this chat not sending me to a video 🤦♀️
that is my bad
So i understand the core concept but where would i stick it in mine? It branches at the true or false, so id have to put it before that, but nothing is being "changed" before that...or so i think...
step one, figure out where setting variables and applying them to actual stuff is. step two, divide em with custom event call
That would be this correct?
nope. both top and bootom stuff do things to actual things so i dont see any disivion, nor i see custom event
The only thing going in to it is this
so no customevent still
oh i see what youre saying
setting var is one thing, it can branch and stuff. then comes custom event for everyone that is deteministic
Like that?
also sendChange is useless networking wise btw
uh, but you dont set it before
wait what
where did events ever came to this
oooh
no, just no
yeahhhhh
naming things True and False is just illegal
Really?
i wish
anyway
i cant even can see how you determine if is glass or not
on, its just IsGlass
then thats it
I mean if IsGlass is true, we set to fogged, and if IsGlass is false, we set to glass right
interact - become owner - get "isglass" - unarynegation - set "isglass" (variable only!) - req serialization- custom event "apply mf glass"
ondeserialization - custom event "apply mf glass"
event custom "apply mf glass" - branch "isglass" - true set whatever, false set whatever
given your interact button is just on/off
if you have button per mode then its slightly more
wdym when you say get "isglass"?
@tulip sphinx So something like this?
love me android so its very lowres but looks decent👍
Ill test it out! Thank you very much!
I love the effect of not knowing how anything works but the second i see it made im like oh yeah that makes sense lol
ye was about to tell to set IsGlass to synced
ah also sets in the end are useless
if not harmfull
you generally normally dont manipulate variables in "everyone's" custom event, its just to apply variables to more meaningful things
can you perfectly recreate vrcobjectsync in udonbehaviour so that you dont have to rely on network ids and be able to sync multiple objects with 1 script?
Problem with that is you're creating one huge serialization which increase latency for data transfer.
not that huge, 10 bytes per object
and loading 1000 objects into it seems more sane than holding 1000 placeholder vrcobjectsyncs in the scene?
If it's 10 bytes per object then you aren't recreating a perfect vrcobjectsync.
well do we have examples of vrcobjectsync in udonbehaviour then? i assume it holds some velocity and other stuff
There is also angular velocity and kinematic and gravity state too.
there are a couple people that have done this for different reasons, pretty sure occala did, if you look up a bit in #udon-showoff
not sure if they published theirs anywhere
i made something similar a couple years ago and i've seen a couple others, but don't remember who made thoses
vrcsync is an unique type of sync, that doesnt send anything when idle (but sending up to 5hz when moving) so recreating it perfectly doesnt look very doable?
i mean world full of them tends to lag more even when not moving so i dont know about that
i'm pretty sure they do something like if (isowner && notSleeping) SendLoop()
not impossible to recreate on manual
mine isn't public, but yeah you can route things through one script and handle instantiation yourself and late join propagation. some people end up using synced arrays to place static objects, with the downside of it being type specific and difficult for dynamic objects
its not really a recommended thing to recreate it. and as someone mentioned before they do not update once idle. which normally takes a few seconds or so after they stop moving.
even if you managed to recreate it you would have to deal with the Limits of Manual Sync and it's inherit higher data consumption due to the handshake needed to by anyone who receives it.
and it is more efficent to have 1000 of them precreated and in the scene before hand. Since you do not need to spend resources and time on intializing those 1000 objects. Since u can just disable the render when not in use. or combine it with the vrcobject pool to disable them etc potentially.
It doesn’t work exactly the same as object sync, but the way you can achieve something similar is by putting all your interactive objects in a single array and syncing the index of the item that was grabbed, so other players can see you’re holding item index 2 and they’ll place the item at your hand position locally each frame
this means the only time you’re network syncing is the quick notification that something was just grabbed or dropped
but I always end up integrating this with lots of other code so I can’t just post the code for this solution, it’ll take some work for you to recreate
the position (Vector3) and rotation (Quaternion) would be 28 bytes in itself
also in my testing the max amount of simultaneously active vrcobjectsyncs is ~40, no way are you getting 1000 working
also by combining them all together you're losing out on the main advantage of VRCObjectSync, it can sleep its individual object while it is not moving thus causing it to not consume networking bandwidth
VRCObjectSync uses a Continuous sync-like sync when it is active. Continuous is notably a more lightweight sync method than manual sync and can support a higher world-wide update rate.
At a baseline you'd need to create a manual sync object that controls (enables and disabled) a continuous sync object.
@north thistle how did you got 40, i was piling up physics enabled and got suffering somewhere around 300
and thats active ie not sleeping, so 1000 overall still makes sense most cases
What do you mean by "physics enabled"? How were you forcibly moving them?
they were just piling up after i mass spawn them. i mean not kinematic ie they collided and affected each other
I'm pretty sure them simply being in contact with another object doesn't awaken them; they have to actually move to be awoken otherwise they sleep
nope, they use rigindbody sleeping property. ie unity uses sleeping state to save physx calculations, vrc re-uses it to pause sync on top of that
oh i mean just being in contact doesnt keep them awake sure
but its a mass spawn of 300 things in one place so they need a lot of time to wiggle down
also did math and it made sense. like probably uuuh 32x7x5x300/8 is 8KBs+whatever overhead
last i checked vrcobjectsync uses 100+ bytes when its not on idle
it won't use a fixed usage rate as it has a dynamic update rate
this was with a simple rigidbody.
and it always used 100+
world entering light suffering from 50 VRCObjectSync objects under the most optimal of situations (nothing else in the world using networking; only 1 other player in the instance)
light? you hit max out all the time thats heavy suffering
"lightweight" in that it doesn't clog on more than some count per second like manual?
vrc's object sync runs an update loop, always, presumably to check for sleeping status/movement to wake their sync loop. this is logically heavier
you can avoid it entirely if you only wake the sync on relevant events (pickup events and collision events for example) and subscribe to an opt-in loop
in my own handling i run the sync through a central manager which makes it not beholden to the ~40 manual per second rate anyway, using network events, though this isn't really necessary for manual handling
continuous is necessary if you want it at object level with active counts > ~40/s, which i'd imagine might not be the case for someone wanting 1k objects around (many would typically be inactive)
you can also control the rate and data sent if you run your own sync, which can be lighter on bandwidth, or heavier if you need, you can control the interpolation type or handle it differently as needed
using their object sync is valid, it's probably not an option for every creator to invest in making and running bespoke handling; but i'd state my preference for controlling every layer
you will be heavily limited with manual. and you would never be able to have many active.
i did specifically state that in the case for continuous
I'm talking lightweight in terms of networking
i would disagree data-wise, i'd only agree on the merit of manual having a per second object serialization limit
you would hit data limit faster through. since the handshake each time
i run through a central handler myself, meaning i only pay the ack once per bundle, but there's a trade-off to be made in manual handling, at some point manual loses out in an environment. i don't think either is absolute or "the best" in all cases
handler of what. if you want any sort of data owned per person that isnt viable
what in the. it is like 220bps per sync indeed. and its 27B (16B pos, 20B quarternion - with overheads, 1B who knows, so for apparently new standard 4hz its already 108Bps). And also my math doesnt add up at all (or rather it does and shows 42KBps without overhead)
still, its per player and active so 1000 doesnt sound unreasonable
a central router, it isn't owned by a specific person
i should clarify, it is technically owned by the master i suppose
you're allowed to own an object at object-level and route the data differently
My previous example shows light suffering at 50 objects with VRCObjectSync
Doing a similar 4hz update rate with manual sync induces heavy suffering at just 13 objects
Manual sync alone can in now way serve as a replacement for the use case of VRCObjectSync
(note that the suffering would increase seemingly without ever stopping if not for my manual sync skipping a sync attempt if it detects that the network is suffering)
i've been saying that i use a central object for passing the data myself; 13 objects is quite low though, it might be that manual at object level is less viable than i'd expect. i don't know that i feel like testing it myself this instant. are you saying you had 50 moving on manual or just 13 moving on manual?
also manual sync incures a remote upload cost on everyone else in the instance while VRCObjectSync does not, for instances where that matters
the first clip above is testing 50 VRCObjectSync objects
the second clip is 13 manual sync objects
for context 12 manual sync objects work just fine
VRC really doesn't like performing many manual syncs per second world wide
oh you're hitting the manual limit there yeah
the kb/out is only ~5
manual is less viable at object level then, for nav agents it's probably preferable to cluster them or use object sync. for rigidbodies i'd* be okay with manual sends at object level unless i expected a lot to be moving at once, but again i use a central event so i don't really encounter either issue in my environment
1000 objects would be 28000bytes of positional + rotational data per sync; if we do an update rate of 4hz that's 112000 bytes per second which is 112kb/s
the upload limit for vrc is ~8kb/s
no way are you ever getting close to 1000 objects
On a side note, if you want a small handful of relatively low latency position syncs the best sync method to use would be network events
Does anyone remember offhand what the calculation is for the byte size of an UdonSync array?
Obviously it is at least the combined size of its constituent members
but I know there's some amount of overhead and I do not remember what that overhead calculation is
Digging through an old testbed project I found that I recorded it as "52 byte header plus the combined size of constituent members, but in 4-member chunks (so an array with 1, 2, 3, or 4 members are all 52 + 4*membertypesize bytes)"
I hope I don't have that wrong
there's also a ton of undocumented mysteries around how actual traffic from manual serialization works because the tools showing you bandwidth usage show VASTLY higher outgoing data sizes than the calculated 'serialization result bytesize' value, even after accounting for the baseline usage from the player themselves...
I don't even think documentation tells you that of the 11 kb/s you have for outgoing traffic before network suffering kicks in, your player and VOIP are already using about half or more of that number...
The most up-to-date place in the documentation for networking limits is contained, unintuitively, in the page for Network Events: https://creators.vrchat.com/worlds/udon/networking/events/#parameter-size-limits-and-event-splitting
"Total outgoing data is hard-capped at approximately 18 KB/s. This includes all network overhead however, so in practice it is unlikely you will see more than 8-10 KB/s."
Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.
Is the 18kb/s outgoing an 'update' from the original 11? Or is the 11 figure an estimate derived from the "18 minus overhead" and just happens to be contradictory to the conservative "actual" estimate on this page?
Also an additional thing cutting into your bandwidth is you having to confirm that you have successfully received network data from other people in the instance; so the more people in the instance the more your "background" upload usage will go up
What? There's actually a handshake??
I had no idea
All these networking systems have felt very... UDP to me
I'm not 100% sure; but I have observed that background upload usage goes up the more people are in an instance; and apparently at 80 people you can't get any world-side networking done with any good reliability
oof...
At the very least the only networking available to Udon that doesn't seem to use a handshake is VRCObjectSync and Continuous sync
Hmm. Did we ever get access to classes that make bitstring editing easier? I forget the predominant class--
I am strongly debating implementing a custom byte array compression to some of my bulkier classes
but man editing bytes manually is a slog
they mean ack rather than handshake, it's rudp
Ah, I'm a little too naive to know the nuance/difference here, I think
handshake would be more tcp land
I once had the Copilot autocomplete use System.BitConverter.GetBytes and I seem to recall that working just fine
yeah BitConverter was the one I was thinking of. I know for at least some period of time, it was inaccessible to UdonSharp
I hope it works now
Im pretty sure i recall using it for a bit in the early stages of my project without any issue
1000 ids, not actively rotating objects. if theyre just props and especially kinematic you can only have like 2 active per player (well and some more while they fall asleep)
the original question was syncing multiple objects with a single "script" (presumably they meant object); that would require having the sync all the objects every time you want to sync one of them
@north thistle well ye thats why the answer was "its easier to create 1000vrc syncs so you have plenty ids than trying to avoid them"
Introducing an Open Beta release for the Spring 2021 Networking Open Beta! This beta is expected to run for several weeks as we gather feedback and continue making improvements. If you're reading this in the pins, click the pin to jump up to this message! There's a few more after it.
Who is this for?
This beta release will benefit all users of VRChat but the main audience is World Creators.
- We encourage you to update your SDK3 worlds to use the new networking abilities in this update. If you're going to do this, make sure to publish with a new blueprint ID so it doesn't overwrite your existing world.
- If you're not ready to update your world, or you have an SDK2 world that you haven't rebuilt in SDK3 yet, this is a good time to test them and make sure they continue to work in this new system.
What's in it?
You can find the details of the new Udon Networking system here: https://docs.vrchat.com/docs/udon-networking. Basically, these updates provide new precise controls for syncing, support for syncing arrays (collections of variables) and fixes for join-in-progress issues. More details for creators below:
- Array variables can be synced (see docs for list of supported Array types)
- New 'Manual' Sync Mode for UdonBehaviours for precise control of when variables are serialized.
- New 'VRCObjectSync' component instead of 'Sync' property on UdonBehaviours if you just need to sync Transform & Rigidbody. Also allows you to temporarily disable interpolation on an object, and properly set the Gravity and Kinematic properties on a synced physics object.
- Networking.SetOwner now works immediately - you can update variables immediately after changing ownership and the changes will apply properly.
- Join-In-Progress issues fixed, you should no longer have issues with variables not being Deserialized for Players who join the world after they are changed.
- New VRCObjectPool for managing collections of objects, including automatically managing their active states. Use this instead of Instantiation when you want a lot of networked objects.
- New Debugging tools for seeing info about networked objects in your world, including the current owner, the data being used, and state information.
How do I use it?
As a creator, read through the new documentation to understand what has changed, download this SDK: https://files.vrchat.cloud/sdk/VRCSDK3-Udon-Networking-Beta-v5.unitypackage, implement new Manual UdonBehaviours, Array Types, etc as needed, test in new Beta branch. The UdonExampleScene has been updated to use the new features, and includes a few new examples as well - for Array Sync, Object Pools, and Pens.
As a general user, you're welcome to switch to this beta branch to test your favorite worlds - but give the creators some time to update things.
To use the beta branch of the application, use the code SyncYourTeethIntoUdon and you'll unlock the Udon Networking Update branch. This won't work with Live!
Any updates we provide will be announced in the #open-beta-announcements channel. To get pinged for updates on this project, please check #open-beta-info and click the
emoji. You'll be assigned the <@&821522584871895051> role and will be pinged any time we make a relevant announcement, such as updated SDKs or etc.
How to report issues?
We've created a new section on our Canny here for bugs and feature requests: https://feedback.vrchat.com/udon-networking-update
When will the full version be released?
When it's ready! 😉 We want to make sure everyone's amazing worlds work better than ever, and no existing content breaks.
🎉
oh?
this looks ncie
pog
@sand goblet wake the fuck up samurai, we've got a city to burn.
pog
yeah panda wake up
no persistence? 😦
Finally yo! New Udon things for me to learn slowly and by that I mean A whole year to learn...but that's just how I learn.
what about udon messaging ping which is around 2000 ms?
Network events overrated
no
tupper give me that.
Long have we waited
before people ask about it - This update does not include Custom Network Events with Parameters. However, it is a necessary update for that feature and many many others 😄
I want some.
Defend me from them that rise up against me
New VRCObjectPool for managing collections of objects, including automatically managing their active states. Use this instead of Instantiation when you want a lot of networked objects. so does this mean we can use this to enable and disable game objects and have that synced without needing to setup a system to sync if something is enabled or disabled on our own?
Now we manually syncing and somehow end this sentence with aited
that's correct
sort of. It's meant to be almost as simple as spawning/despawning. You tell the pool "hey give me an object" then "ok put this object away"
don't forget to go to #open-beta-info and click the
emoji to get pings for updates on this beta
It won't let me have both roles
oh btw tupper is only having one or the other role for open beta intentional?
The pool is solid from what I have tested.
Would like to have both :p
I'll give you thirty minutes.

ya thats prob enough
Restart steam
Steam is bad at fetching new betas at times.
Right so it is half 11 and I have been valiantly attempting to fix my sleep for the last few days. And this server doesn't allow for posting meme GIFs. So imagine I am posting the Undertaker doing his sit up no sell thing
Steam is just that - Steam powered. It's slow.
yeah usually just restarting steam makes things popup
so say I have like, a mirror and I want to sync it enabled or disabled globally (I know, terrible). I can use this pool to add the mirror to it, then if I use a script to enable it, it will be enabled for later joiners too?
Yes
But that is a misuse of the system.
You are better off using a manually synced bool for that
If you want to give an item to each player that joins for example, you'd use the pool
it's meant for large groups of objects
Yeah
For folks that care, there is a LOT of nuance to this update.
I am going to get out some example work in a bit
if it's just for a single object, that's overcomplicating it because you'd still need an udonbehaviour to work with the pool, then the pool itself. If you just use a manually synced bool, all you need is the one udonbehaviour with the one synced bool
I don't think so
has byte been made syncable 😔
wait - dan said it was
ok i think i fix
and I listed byte[] here:
it's certainly in the supported type list
lmk if it doesn't work and I'll update the docs
It's good to go now. :p
wouldn't you need some manager to take care of syncing enable/disable anyways since the udon script can't run on the individual object while disabled anyways? so I would need some "manager" udonbehaviour that then tells new joiners which objects in the scene are enabled and disabled
Right if you lot have byte array syncable then that is v.good news for the FSP.VRCBilliards port.
does it work though? Byte, sbyte, short, ushort have been broken for a long while
they all get deserialized as int on the receiving end, which will basically always crash the receiving behaviour due to heap type mismatches
oh - I don't have a test for it. If those were broken before, they may be broken still
Oh no
Actually before hand the behaviour was specifically that sync would fail
Would throw due to a bad cast
One nerd stat atm
A serialisation is 72b + what looks like 2x the bits actually serialized
You have 18kb and catch up serialization load sits on Owner
Will have that re tested and then written up
We weren't aware of this issue - @frozen igloo is testing now and we'll put it on the list of things to fix.
Oh my god I think I am dreaming .. gonna be testing so much of this today..
We need a VRNow in celebration
true
I look forward to updating ring of fire with this
okay regression benchmarks are gonna take a bit since I have to use VNC from my laptop on mountain satellite internet
expecting it to be the same as closed beta anyway
sooo.... we have reliable sync now? like sync it once and late joiners also get that value? That would really be awesome!
doc clarification - object ownership seems to indicate that OnOwnershipRequest will always be called to confirm ownership transfer but the notes above suggest immediate ownership transfer is possible. Is this just a doc miss for the "event isn't implemented" case?
was too excited and missed this new channel existing.
ah found the docs, never mind 🙂
yeah same
i was mainly confused about the old sdk linked in open beta
@woven pewter From what I understand it is speculative, which means that when you take ownership, on your client it will assume that the change happened immediately. But if the previous owner denies it, then nothing will be sent out to the other clients and it will be reverted
tbf this is what Tupper did and I asked him to move it up here
regardless there should be a mention in the ownership section of the documentation of which circumstances can support immediate ownership transfer and which circumstances can't (and the latency implications of having an OnOwnershipRequest event, if any)
Aaah yes finally can get my hands on the goods.
Very much pog
Though any ideas if it works with UdonSharp ? Prob not Merlin is going to have to release update I'd guess
all ownership transfers can be "immediate" in the sense of "taking ownership then immediately setting a synced variable". The only cases where that wouldn't work is if the previous owner denies it, which is something the author of the script would decide
udonsharp is already updated
30 mins ago
Okay. The other important aspect is the latency side of things - does it take longer (more network round trips) if OnOwnershipRequest is defined?
Well time to get up from my bed and not sleep tonight
From what I've heard, all ownership transfers go through a request-acceptance process, but if you don't override OnOwnershipRequest, it just always returns true
fair enough
would be nice if that could be optimized someday for the common case of "anyone who wants ownership can have it" but not critical immediately I guess... though I do wonder if that'll cause visible lag on pickups
I'm not following, what are you asking for?
oh I see. I don't think this new method is any slower, even with continuous sync. And when it comes to manual sync, it's even faster than network events
I can proudly say that I am the first canny report of the new beta 👍
https://feedback.vrchat.com/udon-networking-update/p/udonbehaviour-sync-method-description-does-not-properly-wrap
so, I don't know what was happening previously, but just from a theoretical perspective, if you have to ask the current owner, you've added a round-trip to the time before the new owner can start transmitting. If that's already happening today, welp, not a regression, but an area for future improvement perhaps.
it would also encourage horrible hacks like using pooled objects to transmit without ownership handovers to reduce latency 🙄
having a pooled object for each player is hardly a hack, there are many very good reasons to have such a system
As a note in my screenshot it still defaults to a non-existent program type if I haven't imported U# yet lol
Does this branch address the lerping issues with pickups?
true, but it's unnatural to use it solely as a transmitter for information associated with another shared object that I don't want to wait for ownership transfers on 😛
anyway, it's all a bit speculatey, I remember seeing danly talking about potentially skipping ownership transfer confirmations in the case where OnOwnershipRequest is unimplemented, so mostly I just want to have confirmation of whether that optimization was implemented yet or not
eh, with manual sync the latency should be plenty low enough for transferring ownership, setting variable, and sending a message
1xRTT is potentially upwards of half a second :/
That is only true if you use continuous sync because it has a very low network priority. The true latency is within a couple hundred milliseconds, it's just that low priority traffic takes forever. Manual sync and ownership transfers are high priority
Yeah, that's an issue for people who use U# currently because it saves an EditorPref with a non-existent index. Would be nice to catch if it doesn't exist and show Graph though
No, I'm referring to speed of light/internet here. With an ownership request model, you have: A -> VRC: Request owner (200-300ms), VRC -> B: Request owner (200-300ms), B -> VRC (200-300ms) Ownership transfer approved, VRC -> A: Ownership transfer approved (200-300ms)
and only then can you start serializing. So that's an extra second or so to complete the ownership handover, if you're network-far from the VRChat servers
This update introduces https://docs.vrchat.com/docs/udon-networking#flagdiscontinuity which lets you skip lerping if you want to teleport something
this can be avoided (and this was discussed in closed alpha), but it's unclear if that was in fact implemented 🙂
Oh, I skimmed right past it. Thanks Momo. Do you know if the loading issues with Stations get addressed by the fixes to initializing stuff?
- Join-In-Progress issues fixed…
As manual sync pushes latency down further and further, that bit of overhead may become more apparent. But as it is in this networking beta, it's already a heck of a lot better than live already
yep, if this is overall an improvement over live, I'm all for it, I just want clarity in documentation on the latency costs of various operations
if SetOwner is "free" (ie, has minimal overhead beyond just manually syncing things) in some situations, that informs my networking design. If it's expensive and takes potentially over a second to complete, that also informs my design.
I'll just put this into a canny post I guess 😛
Does manual syncing perform any other traffic optimizations such as only sending variable data that has actually changed since the last serialization?
it's manual sync, you decide that
They only expose UdonBehaviour.RequestSerialization, but that implies that everything about the behaviour is still synced at once, but no details on what data is actually synced at that point
But what I asked is if they perform any other optimizations to reduce wasteful sending of variable data that doesn't need to be synced
you have control over exactly when things are synced, and all syncs are received, so you can do those optimizations (or not), right? 🙂
If I have 10 synced variables and only update one of them, 9 of them don't need to be sent over the network
Unless a late joiner enters the world
if you have a mode where sometimes some variables are sent and some aren't, you now have some overhead to describe which variables you're transmitting
you can't pick and choose which variables get synced, gotta sync the entire object
you could implement this yourself by syncing a byte[] potentially 😛
however, with manual sync, there is zero networking overhead to have an udonbehaviour just chilling doing nothing. So if you really want to you could have a whole udonbehaviour for the sole purpose of syncing a single variable
that being said, I really don't think that's necessary because manual sync can sync a lot of data very quickly
Does VRC Object Sync sync all rigidbody settings, or just some of them? (IIRC, previously Drag and Angular Drag weren't synced)
posting some old test videos while things where still in closed beta.
here is a example of syncing a color32 array. https://www.youtube.com/watch?v=uggILMn7rrA
and testing latency for different types of syncing. https://www.youtube.com/watch?v=7ouk8RsNCOM
green is object sync, red is manual sync sending data every .2 seconds.
huh, is that manual sync running ahead of IK?
things have changed since the videos so dont take them as 100% accurate.
One more question is about the sync limits, the docs say the maximum output is 11kb/s, is that how much you send out to each player individually in the instance, or is it the maximum for up to 80 players in an instance combined?
per player.
Alright cool
Well time to try and rework literally the whole late-syncing process in my world 😄
Wait hm.. Gotta ask.. Is string[] sync planned ?
@obsidian cedar no, it's not.
:/
we'll have byte[], but our network engineer would rather no one tries to sync all of the bee movie script
😄 Understandable I guess
Data and Specs says that continuous sync is limited to 200 bytes per sync; is there a limit for manual synced objects as well?
and, does the synced string limit apply to manual synced objects as well?
@sudden current
such a call out
🤔
well now that you mention it...
the challenge is accepted
OH and before anyone asks about str len sync
Manual sync has fixed the str max len bug
hooray!
can't you theoretically convert string into int 😄 ?
yes
... 18 bytes?
Yeah you have 18b.
🤔
we can send strings of up to... 18 bytes.
that's slightly different
18b that would be bit.. low
Also I think data inflates by 2x when serialized so you probably cap out at 9KB
probably best to use byte[]



