#udon-networking

1 messages · Page 11 of 1

gilded depot
#

is there a way i can get which player originates a CustomNetworkEvent without transferring ownership? or is that unavoidable?
i'm trying to write a script that adds/removes players from a list on button press, but transferring ownership of the whole object feels a bit excessive
i recall reading somewhere in the documentation about network events passing something as an input, but i can't find that anywhere

strange token
#

That would be cool but does not sound like it would be an option. The only way the system will accept a network event from an object is if the player calling the event owns it

stone badger
stone badger
# gilded depot is there a way i can get which player originates a CustomNetworkEvent without tr...

The key thing here (I think) is to separate the transfer of sync'd data with an object (or obects) that need to act on the data. A little tricky to explain but you can set up "data sync" objects to transfer state. These do not need to be part of the object that uses the data. A list can "react" to the data change on another object. So (for instance) each local copy of a list should be able to update itself when a "sync'd object" that contains (for instance the player id) arrives.

#

Some care is needed to coordinate with late joiners as is always the case with sync'd data.

misty flame
#

Hey guys, I'm new the Networking for this game. I was wondering if you could clarify something for me.

Using UdonSharp, I tried to make a script that teleports all players to a specific location when a single user interacts with a button. The first player would teleport, but when it would get to the second player, they would teleport to the wrong location.

I used RequestSerialization and OnDeserialization to sync these changes manually.

I ended up using SendCustomNetworkEvent instead which worked fine. But why doesn't this work?:


using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UnityEngine.UI;
using UnityEditor;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Teleporter : UdonSharpBehaviour
{
    public override void Interact()
    {
        base.Interact();

        if (!Networking.IsOwner(gameObject)) {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);
        }
        RequestSerialization();
        OnDeserialization();
    }

    public override void OnDeserialization()
    {
        Networking.LocalPlayer.TeleportTo(new Vector3(28.5f, 13f, 18f), Quaternion.Euler(0,0,0));
    }
}
#

Again. Player 1 would teleport to this location normally, but the second player would not.

vapid pagoda
# misty flame Hey guys, I'm new the Networking for this game. I was wondering if you could cla...

you dont need networking for this, RequestSerialzation will only work if you changed an UdonSynced variable, instead use this

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UnityEngine.UI;
using UnityEditor;

[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class Teleporter : UdonSharpBehaviour
{
    new void Interact()
    {
        SendCustomEventDelayedFrames(nameof(_TeleportPlayer), 0);
    }

    public void _TeleportPlayer()
    {
        Networking.LocalPlayer.TeleportTo(new Vector3(28.5f, 13f, 18f), Quaternion.Euler(0,0,0));
    }
}```

you might be wondering, why use "SendCustomEventDelayedFrames", teleport will only work properly when delayed by 1 frame, something something execution order
#

oh my bad, this is single user, you are trying to teleport everyone

misty flame
#

Ohhhhhh. I see. So OnDeserialization is called only when UdonSynced variables are changed

#

and you RequestSerialization

#

I see

vapid pagoda
#

one moment im fixing it for networked

misty flame
#

ok

vapid pagoda
#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
using UnityEngine.UI;
using UnityEditor;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class Teleporter : UdonSharpBehaviour
{
    new void Interact()
    {
        SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TeleportPlayers));
    }

    public void TeleportPlayers() {
        SendCustomEventDelayedFrames(nameof(_TeleportPlayer), 0);
    }

    public void _TeleportPlayer()
    {
        Networking.LocalPlayer.TeleportTo(new Vector3(28.5f, 13f, 18f), Quaternion.Euler(0,0,0));
    }
}```
#

this should teleport everyone

#

you can also use RequestSerialization to ensure it goes through, NetworkEvents arent as reliable

#

but youd need to set up logic for it, this would be easier to understand for now

misty flame
#

Why 2 events?

vapid pagoda
#

the second one isnt really an event, its more like a coroutine, i explained above

misty flame
#

teleport will only work properly when delayed 1 frame...

#

maybe that's why it wasn't working properly when I was using OnDeserialization

#

Because the 2nd player would get teleported to some random location

#

even if I put a specific teleport location

#

With the code that I provided I excluded some of the UdonSynced variables that I was changing.

So that's probably why the OnDeserialization method was running for me

#

but the code I provided, the 2nd player should technically not teleport at all

#

@vapid pagoda Thanks for the help. I think I understand this a bit better now.

strange token
#

Teleports are so weird for that reason!!

#

When am I gonna see a bug fix for “fix teleporting when called from OnDeserialization” lol

vapid pagoda
strange token
#

;-; that’s fair

patent robin
#

Hello ! Out of curiosity, does RequestSerialization only send variables that changed (instead of every synced variables) in Manual sync ?

#

(And if that's the case, do Data Dictionaries only send the tokens that changed ?)

wispy arrow
patent robin
#

Ah so that answers the 2nd question

random parrot
wispy arrow
frozen igloo
wispy arrow
humble girder
indigo bough
#

so I wanna make a button that toogles something off and something else on that can only be done by an instance owner and idk how this stuff works so help

humble girder
humble girder
#

And also, if you use network event to sync object state, you're doing it wrong.

indigo bough
#

all sounds chinese to me

humble girder
indigo bough
#

still confuse

#

cant even find the stuff in this video like state n stuff

#

god im old

humble girder
# indigo bough where I put this?

So you asked where to add.

I had explained "Add a branch in Interaction event". It's your "Interact" node. Isn't that clear enough?

humble girder
humble girder
indigo bough
#

okk

indigo bough
humble girder
indigo bough
humble girder
indigo bough
#

idk stuff

humble girder
indigo bough
#

its surprising i got that far

humble girder
indigo bough
#

howww xddd

humble girder
humble girder
indigo bough
#

poggers

frozen igloo
patent robin
frozen igloo
#

Unless of course the input fields have the potential to be very large strings... In that case yeah sync them separately

patent robin
#

Thanks, that actually makes my life easier ^^

stone badger
#

When data is sync'd there is more going on than simply the transfer of the data values or there would be no reason to sync the values. Something on the other clients change and this can cause needless redrawing and/or other events to fire. But even if it doesn't having your UI components update their appearance when nothing has changed gets you nothing.

wispy arrow
stone badger
# wispy arrow Is this likely to change with the release of Udon 2 (whenever it happens.. _prob...

Concerns over bytes are (mistakenly in my view) expressed here fairly routinely. Networking isn't limited to the few bytes transferred when sync'ing a bool and/or int value. Data is transferring all the time. View the logs and you will see how much data is transferred at login, each time you join a world, move around, pick something up, etc. If people are counting bytes they must have solved every other issue at hand which I doubt 🙂

wispy arrow
#

i mean i feel like udon's biggest issue is the fact it's not wasm yet

#

the networking is okay

#

(though i hate the p2p network style (yes i know there's a proton proxy server in-between), vrchat would have been much better suited with a client-server architecture, like that of roblox)
it's simpler for developers, and prevents a lot of vrchat's networking issues (ahem, if a world master crashes, the game is locked up until that person gets fully disconnected and master status is transferred)

#

i assume the only reason proton (and ultimately the client-ownership system) was chosen was because of costs

#

because it costs a LOT more to run an entire headless server of the world for each instance

#

when instead they can just have a proxy

frozen igloo
frozen igloo
tulip sphinx
#

I dont get why is it not working. Local player ie owner sees the result (i just print owner's name and itemtype value into TMP for now). Remote player with crossed node enabled sees owner's name but default variable value, with it disconnected "serialize" event doesnt fire at all.
Expected behaviour - crossed node is not needed, on each requestserialization im guaranteed to fire "serialize" event for everyone, late joiners fire it as well.
Sync is set to manual, ownership of the object given before enabling the object and is correct.

tulip sphinx
#

I even made simplified test script and it kinda works but also kinda not. Joining or pushing a button should add 1 to counter and print it along with your username for everyone. It works except for some reason joining (ie. Start event) doesnt add 1 to variable, but shows new owner's name and old value correctly for everyone. Why??

tulip sphinx
tulip sphinx
#

Changed. Button (so, local interact event) sends NewItemType value, so NewItemType is changed only for one person who pushed the button. And it doesnt set the owner! Player2 is the one pushing the button and for him "serialize" happens, but he's not the owner of the object, what? Bottom pre/post events fire never.
Okay it sets the owner allright according to ~+8, but prints wrong owner name

frozen igloo
# tulip sphinx I even made simplified test script and it kinda works but also kinda not. Joinin...

I'm not sure about this entire system, but for this specific thing I can say it's because start is too early for you to receive synced data, so it will always be the default 0 at the point in time that you try to add 1 to it. You're not giving it enough time to receive the current count and then add one to it. A good way to do that might be to have a bool that tracks if you've added your number to the count. In ondeserialization, if you haven't, take ownership and add your number.

tulip sphinx
#

But i recieve data alright. I'd understand if it was always resetting to 1 (default local 0+1)

#

but it shows synced value

#

but id rather understand why none of other 3 images EVER fires OnDeserialization

frozen igloo
tulip sphinx
#

Theres button, it's local. It sets local var NewItemType in networked object script thus firing local event. As the result of local event, person who pushed the button becomes owner of the object and tells everyone the value of synced ItemType var, causing everyone to run OnDeserialization to well, update object state with that new value. In theory.

#

I don't get why setting ownership is a bad idea, since person who pushed the button is also one most likely to interact with that networked object later.

frozen igloo
#

Is newitemtype not a synced variable?

tulip sphinx
#

No

#

ItemType is

frozen igloo
#

Oh ok that's fine

tulip sphinx
#

could it be about the same naming for multiple networked objects? they have unique ids/pathes tho, and im on 3.6.1

frozen igloo
#

No that's fine. But maybe something else is causing it to fail to sync. Are these instantiated?

tulip sphinx
#

no

#

but theyre children of pool objects

frozen igloo
#

Are they ever disabled?

#

That could be it. Disabling network objects can break things like late sync

tulip sphinx
#

well since im using onenable, they are initially disabled. pool object enables them.

frozen igloo
#

Why does this need to be in a pool?

tulip sphinx
#

pool=vrcsync pickup, but having all instantiation stuff on pickup as well (so, forced continuous sync) happened to be way to unreliable, so im trying to remake it with serialization and ability to send data now and then.

frozen igloo
#

Why does the thing keeping track of how many people have visited need to be a child of a pickup? Is it on multiple pickups all doing the same thing?

tulip sphinx
#

uh, second screenshot was just a test

frozen igloo
#

Then what's the actual use case

tulip sphinx
#

its pool of pickups, inside of each theres script that syncs ItemType and instantiates object

#

so its 100 pickups but each of them can be of any type

frozen igloo
#

Ohh gotcha, yes I've done things like that too. I would not recommend having the syncing for that be on the pickups themselves, that's going to be very unstable. I'd build a custom pool manager and have it manage an array of item types centrally. You can even reuse itemtype where one of the values is just non-existent, despawned.

tulip sphinx
#

well i have it working with continuous. Now i went for serializtion and it just doesnt fire

#

ofc i can have it outside and instantiate object not to itself but to pickup but why should i, with current setup i can easily use getparent/child

frozen igloo
#

Because it's a far more stable way to do networking

tulip sphinx
#

what way

#

ah, ie single array?

frozen igloo
#

If the itemtype is determined by the pool, you can spawn it and then instantly set the itemtype at the same type. If the itemtype is synced separately from the pool you have to spawn a dummy object and then wait for it to receive its type which might never happen properly

tulip sphinx
#

i dont like single array idea cause then it will certainly cause some clashing

frozen igloo
#

No more than the existing pool already does

tulip sphinx
frozen igloo
#

Syncing is not reliable on objects which are sometimes disabled

#

Having them enabled at least on start and then disabling once they've initialized is at least slightly better, but having their synced data change on remote before you've ever initialized them because they were disabled at start is not good

tulip sphinx
#

why onpreserialization/on post dont fire? at least in sim. i added them to test script that was working and still nothing

frozen igloo
#

Ok you never clarified clientsim, yeah it doesn't do onpre/onpost

tulip sphinx
#

I mean, i just connected them to logs. i cant see udon logs in game can i?

frozen igloo
#

Debug menu 3

#

Or in appdata/locallow/vrchat

tulip sphinx
#

Okaaaay, that actually helped. I literally just enabled objects in editor (so they get disabled only as a child of the member of pool) and now it works. Network sync, late sync.

#

Thank you, time to test it with lots of objects to see if its actually reliable

tulip sphinx
#

Is this where i get to see 11kb limit or not?

frozen igloo
wispy arrow
#

So I'm wondering how I'm supposed to network this the "right" way in Udon:
My map is tile-based and randomly generated when the instance starts (by selecting random tiles out of a list of tile prefabs, and instantiating them) how do I sync this state to other players?

#

My initial idea was an UdonSynced integer array which points to the index of the prefab

var prefabIndex = gameMap[x * y];
#

and then var prefab = Instantiate(tiles[prefabIndex]);

#

But yeah, I was wondering if there was a better way?

plush tiger
#

easier to sync the random seed and just generate the same on all clients.

wispy arrow
#

Never used seed-based random generation before lol

plush tiger
#

the function to seed unity's random number generator is UnityEngine.Random.InitState(int seed)
just generate a random number first and then save/sync that. then when you go to generate your tiles you call initstate first with your number

wispy arrow
#

Okay, I'm lost here, wtf do I do to get my VRC Pickups to sync as well as having a random map generation system?

There'll be 3,136 plots of land, so I don't think a VRC Object Pool is a viable option, cause I'd need 3136 object instances of each individual plot type

#

Then again, I don't think scripts start syncing until their first Start() is called, right?

#

So if they're disabled until they're needed, they should have zero performance impact (renderers will be off, and my Object Sync / Pickups will not be running nor syncing until they're turned on)

patent robin
#

But like we said before, it's simpler to just base the state on the initial seed - then you can take a lot of workarounds

orchid locust
#

does a GameObject always have an owner? Does the owner get reassigned automatically if the current owner leaves the instance?

tulip sphinx
#

@wispy arrow uh, your plots of land are pickups or what?

#

@orchid locust objects with udon on it (and some others like avatar pedestals for some reason). yes. theres docs you know.

orchid locust
#

(a link to the docs would've been more helpful here)

strange token
wispy arrow
#

not the whole plot

mossy ruin
#

wanted to ask in relation to an vrc object pool, if the items in question are manual networked items, is there a chance they wont get their synced values for a late joiner before the object pool sets them to spawned?

#

like will it have the object active before the networking sets its data, or is there a chance that it wont get the data before its active?

wispy arrow
mossy ruin
#

so if you have it as apart of vrcobject pool, does it handle that?

wispy arrow
#

not sure

#

i'm gonna be using one EXTENSIVELY for my project, so I can let you know how it goes with regards to networking things lol

mossy ruin
#

thanks, i wanted to use it to optimize how im doing my dynamic items. Right now they just all exist, so i tried to optimize if they are in stand still to do nothing, but it could be a step better if they were handled by a vrcobject pool

#

but im debating weither or not that would actually be a help or hinderance

wispy arrow
#

I mean, a pool will disable the objects when they're not in use, and you just fetch them as you need them

#

i think that's pretty good for performance

mossy ruin
#

maybe, right now in idel they wont have any active mesh renderers, so they wouldnt eat up the gpu, but they do have post updates with a bool to prevent it from running so they are still doing an if statment a frame

#

so def would be a bump up, just not sure by how much XD

wispy arrow
#

Yeah, if an UdonBehaviour has networking (either by Manual or Continuous sync), it WILL consume more CPU frametime than an entirely unsynced one

meager meadow
#

I know when I've used manually syncd objects with a pool, I had to account for values being set and never receiving an OnDeserialization(). Of course you'll also need Ondeserialization() or similar for when its active, so you end of having to code for both possibilities anyway.

mossy ruin
#

hmm....sounds like a headache...i think ill look at it as an option if i find i need to save more cpu

#

funny enough 600 manual sync objects doesnt consume a lot

#

assuming they are idle

meager meadow
#

Cool. The other headache, in case you end up going this route, is that you'll likely need to do something in OnEnable() (when it comes out of the pool). The first call to OnEnable() happens before the call to Start(), so you can't rely on any initialization you normally put in Start() having occurred.

mossy ruin
#

ah right

tulip sphinx
# wispy arrow my plots have items that can be picked up

well then you messed up since a. every networked stuff uncluding pickups must be in the scene already b. 200 pickups (with vrc sync) is the adequate limit, better less. not saying its impossible but lotta work to make it work

wispy arrow
tulip sphinx
#

you have 11kB per second and for the reasons dev only know it sends it every frame despite pickup being dead. so 200 items having global transform ie vrc sync is the most you can ask for without addding layers

wispy arrow
#

i mean, i'll probably end up doing a custom pickup system because of the way i need it to function, so that's probably no issue, just a bit weird how we don't have more of these seemingly obvious features

tulip sphinx
#

i have plain as bord synced variable with on deserialization, and it doesnt sync on join. why indeedvrcAevSip

wispy arrow
#

and i have this global day/night cycle

#

this should not need this much code

#

it's not even that much

#

but in any client-server program, it wouldn't need all this master bs, because the server will always be present

humble girder
wispy arrow
#

It'd be easier if they were just, the same, but yanno

humble girder
wispy arrow
#

Ooooh, that seems pretty smart

#

And that's guaranteed to be the same for every single player? (Obviously, accounting for differences because of people's latency to the server)

humble girder
wispy arrow
#

Yeah, and I mean as long as it's not like, minutes out of sync it's fine

frank fjord
#

Hey, so this mystical 11 kb/s max outgoing bandwidth mentioned in the VRChat docs... I seem to recall someone at some point mentioning that this is a given client's total max outgoing data, and includes the player's IK data, animator synced variables, and VOIP. I can't find documentation that confirms this, however, and I would REALLY love to know what the numbers are on how much of that data players have been actually observed to use up on their own before any udon networking is added to the pie.

#

I've written down that whomever told me this suggested that players already use on average 4-5 kb/s of this 11 kb/s limit without udon. Again, I can't find any documentation that confirms this so I don't know how much variance I could expect from that number, but...

#

... if that's true, it's wildly misleading to tell world creators that their limit is 11 kb/s if realistically it's usually half that?

#

My main concern is, even if I trust these numbers, what is that 4-5 kb/s based on? How much does VOIP contribute, and how much variance can be expected between a really optimized model with a simple rig and animator, vs a more complicated rig and a beefy animator that's sending data synchronization super frequently?

#

Knowing whether my reasonable UDON bandwidth ceiling is 11 kb/s, 7 kb/s, 5 kb/s, or even less? is a BIG deal when speccing out a networking plan for a game world...

frank fjord
#

Got my answer. Estimate is that clients in VR start at ~3.5 kb/s, plus 0.5 kb/s for voice, plus up to an additional 2 kb/s depending on how bad their parameters are thrashing.

#

So if I want to be extra conservative and avoid network 'suffering' I'm gonna target 4, maybe 5 kb/s absolute max sustained network traffic, from udon, per client

#

Definitely feel like this should be documented still.

tulip sphinx
#

uh avatar complexity doesnt matter (as soon as its fbt humanoid ofc, it can be less), its anyway limited to ik tracking points and 256 bits at 10hz so worst case, avi params take .32kbyte except open puppet menu but its like one-four 8bit params atm

vapid pagoda
frank fjord
#

Phasedragon

wispy arrow
#

Why was 11 kb made the limit, anyway?
"Similar platforms" can go up to 100 kb/s, and yes, while their network architecture wildly differs from VRChat's, it makes sense to allow a more unbounded network throughput, since almost every home network is at least 1 mbps both ways

#

(that mentioned platform is Roblox, lol)

lone zealot
wispy arrow
#

i could probably understand 11kb/s for the world, and a different amount for avatars, and that's merged into one larger maximum

#

but some games have to transfer a lot of data as a part of how they work, and that's quite difficult within the constraints of trying to be within this network limit which literally varies based on what avatar someone's wearing

#

cuz the more IK stuff someone has, and the more synced params, the more network traffic, which means less for the world

tulip sphinx
#

How and why this fails? It should always be true in client sim, it should be true for id 1 in VRC unless ownership has transferred. But, it just isnt. I removed check and everything works now, since non-owner cannot ask for serialization anyway and im my case thats all i need, but why.

tulip sphinx
#

Also what's reasonable time to delay Start event to be sure OnDeserialization fires first?

#

So if they do same stuff Start doesnt use default pre-sync values

cerulean zealot
#

Why don't you just put it on OnDeserialization and have a isFirstTime boolean that is flipped, so you can call an event after the first deserialization?

#

@tulip sphinx

tulip sphinx
#

cause i need to run it for master (ie before any serialization) as well

cerulean zealot
#

Can't you do that with preserialization?

#

Are you trying to mix network event callouts and variables serialization. That's always runs into race conditions for me

tulip sphinx
#

you lost me, and those (pre/post) dont work in sim so wont touch it.
Lemme explain, i load img from url, url is synced but if i load it on Start, it uses fallback value cause OnDeserialization fires afterward and game is already busy loading fallback. So late joiners see fallback, unless i delay loading via Start on X seconds so OnDeserialization's part of loading fires first and Start part gets igonred

cerulean zealot
#

Oh you have to manually run the pre and post serialization on the client Sim.

#

In the "run events" drop down on the gameobject

tulip sphinx
#

I still don't get what am i supposed to do. All i need is to run X once. After serialization, ie with proper networked variables, if there was ever one. Otherwise asap.

cerulean zealot
#

Oh in that case can't you start -> send custom event delayed?

tulip sphinx
#

So, whats the reasonable time

cerulean zealot
#

Idk. I think 20 - 30 seconds and you might be good. Start happens WAY before onPlayerjoined and even before the fade from black is initiated.

tulip sphinx
#

huh. I set it to 5 seconds and while working, it's already suboptimal

#

I.e. i manage to run upstairs and see blank

#

What if it was right in front of player's face on load

cerulean zealot
#

oh if this is something that players first see you may have to do some trickery like, also have your surrounding start black and then fade out.

#

like that breath of the wild world. you start out in blackness and hear zeldas voice "link! wake up link!" then you fade into the environment. that gives some extra time to get things loaded.

wispy arrow
#

I have a player object pool with a player state script
I also have this networked event which is called for everyone, but Owner is never equal to Networking.LocalPlayer, which is causing the tag to appear for the person who whistled (which is NOT what I want)

humble girder
wispy arrow
#

It's handled by CyanPlayerObjectPool, or it's supposed to be.

strange crane
#

What is the best way of syncing a local ui / local variables to a networked script for syncing.

patent robin
# strange crane What is the best way of syncing a local ui / local variables to a networked scri...

Hmmm... here's the way I do it

The local player changes the UI/variable:

  • detect changes on the local UI/variable (for an interactive UI component you can use its events)
  • check if the local player has authority on the networked object
    • if no, SetOwner(localPlayer) if you can or refresh the UI/variable from the networked object
    • if yes or just set owner, update the network object with the UI/variable and RequestSerialization()

A remote player changed the UI/variable:

  • refresh the UI/variable from the networked object in OnDeserialization()
strange crane
# patent robin Hmmm... here's the way I do it **The local player changes the UI/variable:** - ...

Local UI: https://gist.github.com/AvocadoVR/934cede6b5d7aeaeabd6b14fff656717

Start Game Code (Executes First)

        public void StartGame()
        {
            if (!Networking.IsOwner(gameObject)) return;
            Debug.Log("Game has Started!");

            hasGameStarted = true;
            sync = -1;
            RequestSerialization();

            GenRound();

            SendCustomEventDelayedSeconds(nameof(SN), 5);

            mainMenuUI.SetActive(false);
        }

        public void SN()
        {
            Networking.SetOwner(player, networkManager.gameObject);
            networkManager.StartGameNetworking();
        }

Networking Code for Local UI (Executes 2nd)

        public void StartGameNetworking()
        {
            for (int i = 0; i < gameManager.promptAndUsers.Length; i++)
            {
                promptIDs.Add((int)gameManager.promptAndUsers[i].x);
            }

            var players = playerManager.players;

            for (int i = 0; i < playerManager.players.Count; i++)
            {
                var _player = gameManager.players[i];

                VRCPlayerApi currentPlayer = VRCPlayerApi.GetPlayerById(player.playerId);

                if (!Utilities.IsValid(currentPlayer)) return;

                Networking.SetOwner(currentPlayer, gameObject);

                Debug.Log(players[i].Double);
                int playerId = (int)players[i].Double;

                _player.playerId = playerId;
                _player.RequestSerialization();

                EnableVoting();

                playerUI.promptID = gameManager.userPrompts(player.playerId);
            }

Executes Third
ShowPrompt()
playerUI is the local ui

Gist

GitHub Gist: instantly share code, notes, and snippets.

patent robin
#

Sooo... what's the issue you're having ?

strange crane
#

It dosen't seem to be updating the local ui

#

It should be updating the text.

patent robin
#

Does everyone have the same prompt, is the prompt not initialized, or does the UI not show at all ?

#

I think the issue here is you SetOwner and RequestSerialization immediately in a loop

strange crane
#

Ah

#

Well I have a player data script for each player.

patent robin
#

I think you'll be better off making the player data synced so every player owns their own data.
Don't change the owner of the managers (or do it safely by waiting for confirmation) but use networked events/a syncing var on the player data

strange crane
patent robin
# strange crane How. I use a join/leave system

Create as many player data objects as there can be players and place them in an object pool. Reference the pool in the manager, and then:

This doc covers Networking Components, Properties and Events you can use in your Udon Programs.

strange crane
#

set them as an owner or smth

patent robin
#

That too

timber ferry
#

i'm not really sure why this isn't working as intended. everything works object-owner-side, but remote players don't see the cars getting enabled

#

on the remote player's client, i see the logs and it says the correct carIndex and that carEnabled = true, but the gameobject isn't getting enabled

stone badger
# timber ferry i'm not really sure why this isn't working as intended. everything works object-...

I see progress in your coding but you are still (in my opinion) working way too hard to accomplish routine tasks. It may seem non-intuitive but you are not really trying (or shouldn't be trying) to write something that enables a car. Write some code that absolutely, positively sync's an int and a bool. With that working what you do when the sync occurs is up to you. The sync code is guaranteed to work and you reduce the surface area when trying to figure out why this particular object doesn't enable.

#

Note that you call CarSpawnLoop for everyone and then check if the localPlayer is the owner. But you needn't call the method at all in that case right?

#

And (hopefully you take this as helpful) while you logging is better it is still way to complex. You shouldn't need to pass arbitrary strings like "aqua" and "orange". These mean something don't they? Are they different types of log statements? Consider adding other methods to your Logger. like LimeLog(), OrangeLog(), etc. if you absolutely want to indicate the color.. These simply call ColorLog and pass along the color. What do the true and false parameters indicate? Again make them available via more static methods so you don't end up sending them all the time.

timber ferry
#

i think part of my issue with this script specifically, is that i was trying to convert a (not that great) graph to U#

#

and, i have my logger set to accept a name, message, color, bold, and italic. that's what those extra parts mean

stone badger
#

That may be related but that will happen all the time. I often translated from one language to another, Z80 ASM, BASIC, Pascal, C and others. You need to keep what your goal is clear regardless.

#

What is the value of the bold and italic when it comes to tracking what is going on in the code? What is a lime true false tell you that an aqua true false doesn't?

#

I wrote a color-coded logging system in JavaScript but I don't pass all the colors and stuff. The colors are used to denote the "level" so Info, Warning, Error, Trace, etc.

#

One just doesn't want to have their logging system get in the way of writing and reading the code that matters.

#

Even the colors BTW, if you insist on using them might be better off defined as enum values. You don't need strings for this.

vapid pagoda
#

or be goofy like me!

dense spade
vapid pagoda
dense spade
#

But string interp

vapid pagoda
#

i dont know what to tell you, to each their own

stone badger
#

Of course everyone should write code the way they prefer but it is nice to see alternatives particularly if the options offer simpler, more robust solutions. The following is one of my calls to my Logger class.

#
Logger.Info($"{_instanceId}:{_nameofClass}.{nameof(Start)} Version={AppApi.AppVersion} IsQuest={AppApi.IsQuest} IsDevelopment={IsDevelopment}");
#

it outputs the following (minus the color strings which I've removed from the post)

#
[app][18:07:20.12] 27068:App.Start Version=v 0.29.1 IsQuest=False IsDevelopment=True
#

The color is defined by the fact that I asked for Info logging, both the time and the colorization are optional.

#

Plus it supports the typical logging levels plus "any" meaning output it regardless of level and Assert which will only log if the assertion is true.

#

The last one can be useful in say Update methods where you may only want to see a log a minute or so and not every frame.

#

With subclassing which I make heavy use of the instance Id lets me group the subclassed logging

timber ferry
#

i also re-did my custom logger a bit to use a color enum instead of string, it only takes one bool for bold or not, and i only have one method (Log) because i didn't really use my other two i had

coarse whale
#

how can I make an instantiated object synced for all players in my world? I tried using networking.instantiate but it says its not exposed in udon
I've also tried regularly instantiated an object with a vrc object sync component but that isn't working for some reason

humble girder
vapid pagoda
#

if youre a beginner, create an object pool, theres many cool things you can do with it still

wheat carbon
#

Since my last Update of the SDK my NetworkIDUtility isnt working anymore. It wont let me Import the IDs from my PC World into my Quest world. It throws this error, for every ID it tries to import:

VRCNetworkIDUtility.<DetectConflicts>g__DoTypesMatch|28_6 (VRCNetworkIDUtility+NetworkObjectRef scene, VRCNetworkIDUtility+NetworkObjectRef loaded) (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:550)
VRCNetworkIDUtility.DetectConflicts (System.Collections.Generic.Dictionary`2[TKey,TValue] loadedRefs, System.Collections.Generic.List`1[T] conflictList) (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:520)
VRCNetworkIDUtility.OnGUI () (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:297)
UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition) (at <80a8ce1980c648dca8e68f0d8aa3b930>:0)
UnityEditor.DockArea.DrawView (UnityEngine.Rect dockAreaRect) (at <80a8ce1980c648dca8e68f0d8aa3b930>:0)
UnityEditor.DockArea.OldOnGUI () (at <80a8ce1980c648dca8e68f0d8aa3b930>:0)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <332857d8803a4878904bcf8f9581ec33>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)```

Any Ideas?
#

It Shows the Window (Where you can Click "Accept all" and "Ignore" those, who arent in the Project. And when Clicking Accept, it throws all the errors. And the IDUtillity Window stays Empty.

#

Also I don't see the IDs in the WorldDescriptor anymore.

#

How can I keep my World in Sync now? Does it safe the IDs somewhere so I can put it Manually there?

open token
#

question, when a player leaves the game who dose the objects they had ownership fall to? is it random? dose it go to the instance master? im verry curious and dont rlly want to test it for myself. what about when a quest player desides to put their headset down and it enters that bugged state? how do you deal with that?

tulip sphinx
#

used to be random, now it kinda leans towards pc/low ping person, still random overall tho. bugged questies transfer ownership without leaving.

#

if you need it for master start button or smth, make counter that unlocks, ie if master have not started the game in like 5 min, anyone can start. for public, waiting even 5 min even if its genuine afk is a lot and in non-public shouldnt be an issue at all

frozen igloo
#

It's never been random, it used to always be the person who has been in the instance the longest, and that person was the master. There was a change so that when the master leaves, it goes to someone with a more stable connection rather than the oldest person. But regardless of how master is determined, it has always been that ownership of orphaned objects goes to the master, whoever that might be

timber ferry
#

what kind of sync mode am i supposed to use if i have a getter/setter?

#

i have this script that uses a getter/setter, but it isnt syncing properly. the Cost and VoxGain isn't increasing for all players, only the player who clicks it

#

(Vox and VoxGain are both synced getter/setters too)

strange token
#

@timber ferry I don't see any serialization requests in there

#

Each time you want to push networked changes with a manual script, you have to run RequestSerialization()

timber ferry
#

i didn't think i had to if i used a getter/setter

strange token
#

The point of manual is all serializations become manual, afaik

#

I've never heard of clients receiving anything over the network without serialization used o.o

obtuse echo
# timber ferry i didn't think i had to if i used a getter/setter

Okay, yeah, I had to look this up again, but FieldChangeCallback is not directly a neworking concept. However, the setter will get called by their networking system when the target variable is synced, aka RequestSerialization is used and the variable changed.
In other words, it doesn't do it for you.

timber ferry
#

okay, so i need to Request Serialization after changing the variables then?

vapid pagoda
timber ferry
#

good to know

timber ferry
#

i added a request serialization before the UpdateInteractText in Upgrade(), but it still isn't syncing the cost or VoxGain for everyone

obtuse echo
timber ferry
#

yeah i know, but i was wondering if i had to do manualClicker.RequestSerialization();

#

which, it sounds like i do

obtuse echo
sullen blade
#

Is there any way to get the current world owner for Udon Graph scripts

timber ferry
#

world owner, or instance owner?

faint rock
#

It's under Networking

strange crane
#

Does anybody know how to do a local ui/object sending data out to a script that syncs that data.

pine prism
#

how can i sync a random number with everyone in my game?

cold laurel
pine prism
#

okay i can translate this to graph easy..

#

but what is requestSerialization?

vapid pagoda
pine prism
#

ah okay

#

ima try this out then

cold laurel
# pine prism ah okay

Remember to set your script to Manual sync mode, and to use the OnDeserialization event!

pine prism
#

like this?

#

ive never used the serialization stuff so idk if this would work😭

cold laurel
# pine prism like this?

That's not quite right.
OnDeserialization Is fired right after you've received synced variables.
This is where you want to do stuff that you need the synced variable for.

pine prism
pine prism
#

also what does this do?

tulip sphinx
#

synced makes it smth networked, only synced values get to be sent on deserialization or on constant sync. smooth will basically smooth the value to the new one over sync latency time or smth (ie in a fraction of a second), usually usefull for gradually moving objects so its smooth on remote (every pickup also uses same interpolation), pointless or harmful for toggles etc.

pine prism
#

so i have this system where you interact with it and it generates a random number which is displayed for everyone. if anyone who isnt the game master interacts with it however, the number previous disappears only for them. how can i fix this?

restive vapor
patent robin
#

Hello, how does networking behave when a child object has a different owner than its parent ?
In theory, here the parent (red) could have a different owner than the 2 children (purple) but I don't know if that's allowed by Udon

patent robin
#

...Nothing in the docs say otherwise, so guess I'll roll with it unless it creates some critical issue ¯_(ツ)_/¯

fresh stump
#

Hello, i have this: if (_CheckAllowedAdmin(Networking.LocalPlayer.displayName))

#

How can i change it to get the ID?

strange gyro
#

By default every gameobject is owned by the master until you call SetOwner on it

strange gyro
#

Keep in mind it's only the ID of the player in the instance, not a globally unique ID

#

If they leave and rejoin the ID will change

#

displayName would still be the most reliable way to check for specific player permissions

violet herald
#

How do I make API calls to external URLs in Udon?

#

Or just connect to them in general.

harsh pebble
#

You can only load in data not send any back

violet herald
violet herald
raw umbra
violet herald
#

Well there goes my idea. Still good to know. Thank you.

raw umbra
#

Of course no problem

patent robin
#

Hello, I have an issue where Networking.IsInstanceOwner is false in the Start() method even when I just created the instance
It's not present in the docs, so is it because that property is not available in U# ?

patent robin
#

After trying other things (deferring the initialization, comparing local player to Networking.InstanceOwner) it just seems plain broken.
Will try with a minimum repro script

patent robin
#

Okay, seems like it's my script specifically - it works fine on an empty project
Still very strange, I hope it's not some kind of very hidden edge case

patent robin
#

Sorry about that, but I really can't figure out what's wrong - everything in this script looks like it should work, and it does work in the editor but not ingame.

  • first image is in editor, switch can be interacted and displays the correct text
  • second image is ingame (I created the instance), switch is disabled and shows the text for non-instance owners
patent robin
#

Aaaaand seems like it's the world itself that breaks it ???
The same test script that worked fine in my test world broke when putting it in this world

patent robin
restive vapor
#

hello, im having a problem i havent been able to fix after like hours of testing and trying things, its meant to be part of a more complicated shop script but i cant even get this part working right. Basically there are multiple buttons that can affect the off and on state of a object, one script toggles on the bool in the script directly which then gets a specific object from a game object array and turns it on, and the other script targets the boolean on the main script to also turn it on, but it isnt working right. When one player presses the 2nd script (the one using setprogramvariable) it doesnt toggle the object for everyone, just the one who pressed it. But oddly as long as the first script (the one that changes the bool directly) is used once the 2nd runs networked just fine? im very confused

faint rock
#

Public sessions and Group sessions have no instance owner.

#

It's a bit annoying. You probably want to use instance master, not instance owner.

#

You could also try manually keeping track of the first instance master, who would in reality probably be the instance owner (but not always, e.g. if someone else runs through the portal to this instance before the person that created the portal).

#

The reason the canny is invalid is because this is the documented deliberate behaviour.

dense spade
patent robin
#

Thanks for both of your answers !
...I'll try making that switch a bit more flexible by locking the owner to the last person who activated it. Will be a bit more complex, but worth it because it'll be less arbitrary.
And yeah about it being "documented deliberate behavior", I've looked in both the Networking and Networking Components pages of the documentation and there's nothing about this. It's actually the opposite, I thought it was more reliable (for my use) than the instance master as written here: https://creators.vrchat.com/worlds/udon/networking/#the-instance-master

Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.

strange gyro
#

Invite, Invite+, Friends, Friends+ - true for the instance creator, otherwise false.
Group, Group+, Group Public, Public - Always false.

pseudo mortar
#

ok so im trying to make an udon graph that can toggle an array of objects (for certain things i need multiple objects to be toggled on or off and can not use a parent object and just toggle a single thing) and in doing so i am struggling to make it sync with late joiners. If anyone here knows how to fix what i have please let me know how to fix it. Its probably something stupid because ive been working on this for too long.

tulip sphinx
# pseudo mortar ok so im trying to make an udon graph that can toggle an array of objects (for c...

thats one hell of a graph🫡. first, theres no sync for people who already in the instance k found it, second, why it iterates through an array to call network event (thus calling it multiple times despite each of them doing the same (or opposite) thing), third, you should use serialization and not network events.

What is your goal again, just toggle x objects on or off (ie just two states A/B) globally for everyone by anyone? Or each object can be toggled individually? Or not everyone can toggle them?

Theres few types of toggles including synced and master that come with u#, click add component - type toggle, select fitting one, populate an array. That would be enough for you if it is just A/B toggle. Otherwise ye, explain how you see it.

strange gyro
#

The late join sync logic is pretty weird:

When the late joiner loads in, they fire off a network event to everyone in the instance multiple times, once per object in the array, and then everyone receives all of those events and iterates through the object array for each event that was fired

#

You should use a synced variable to store the toggled state and update the object states using that variable from the OnDeserialization event

#

And in your interact event take ownership, set the toggled state variable and request serialization to sync the change to everyone

#

Late joiner syncing will be handled automatically with that setup

pseudo mortar
# tulip sphinx thats one hell of a graph🫡. ~~first, theres no sync for people who already in t...

for starters im going to clarify i am very new to udon graph if you couldnt tell already. (to be clear i know unity decently well and blender and what not but i havent bothered learning more about doing stuff like this until recently) and i was sort of trying to combine what i saw in this video https://youtu.be/O3VeBzV9HgI?si=2MGbjhSEd0gsm4-a and add an array.

As for what my goal is i am wanting to create a button that when pressed will toggle all objects under the array (specifically setting them to the opposite state of their current state. For certain use cases i would like some objects to turn off and some objects to turn on via a single button press) and of course syncing that and making sure its synced for people who join after the fact.

I was also using this as a chance to try and learn some of this stuff but if there is a thing built into the sdk then i seem to have wasted a lot of time.

So this took me longer than it should have.
Time to toggle objects again, but this time, for everyone! You'll see that syncing is a little bit more complicated than it was in SDK2, so to set everything up we have to tell every player to toggle that object, but also catch up anyone who joins in after the button was pressed!
All assets are availab...

▶ Play video
tulip sphinx
pseudo mortar
restive vapor
#

Trying to figure out why the second script isnt toggling the object for everyone except the one who pressed the button (not networked) until the first script is ran once, can anyone help?

tulip sphinx
#

request serialization works only if youre the owner, so calling for it from another script is questionable.

#

you want to set variable + run custom event, then in part 1 do event that sets owner+serialization

restive vapor
#

Ahhhh i think i get where i messed up noww, ill try that when i get home

patent robin
#

Hello ! Two questions at once, sorry about that:

  • Do you know if OnPlayerLeft is emitted on the player that's leaving before it actually leaves, or is it only emitted on the remaining players in the instance ?
  • When the owner of an object leaves the instance, does it transfer ownership to the master and (if yes) does it do it before or after OnPlayerLeft is emitted ?
    (After yesterday I'm trying to make my "editing lock" independant from the instance owner and instead allow any player to use the lock - but I'd like to reset the lock when the current owner of the lock leaves)
restive vapor
#

I think i just assumed setting owner on the 2nd part would carry over to the first part oh man a week of pain is over finallyyyy

patent robin
raw umbra
patent robin
#

Oh, instance owner and not master ?
Had issues with the instance owner in public instances before, but the way I coded it now it shouldn't be an issue
Thanks for the info though

tulip sphinx
# raw umbra That changed recently now its not always the instance owner

actually as mods explained me here, they changed that master is not always the "oldest" person in the world now, it can skip questies/other regions and give every abandoned object to someone else, but one single person still. anyway using whole "instance master" is not a good idea, if you need someone to blame just check ownership of some object that never transfers ownership otherwise. and instance owner is another diferent thing that, ye doesnt even always exist and has nothing to do with ownership of objects.

restive vapor
#

do game objects that act as interact triggers need to be toggled on for everyone in order for its script to sync with everyone?

frozen igloo
restive vapor
frozen igloo
#

yeah that looks perfect, that'll sync if it's enabled

#

if you don't want everyone to have it enabled because the gamemaster's panel is disabled, then you'll have to route the networking through to some other object and separate the UI from the networking

restive vapor
#

ohhhhh i didnt think of that thank you!

hollow wagon
#

is there any good explanation how to network/synchronise animations?

stone badger
#

Re: OnDeserialization(DeserializationResult) I would like to confirm my understanding of the sendTime and receiveTime properties. I'm trying to implement a simple "tie window expired" for the action since late joiners will get the sync'd values. If they were not present when it was sync'd I don't want them to take any action.

#
owner: OnDeserialization sent=63.42865 received=63.53965 diff=0.1110001 action=True
other: OnDeserialization sent=60.34761 received=60.47361 diff=0.1259995 action=True
#

Given these 2 log items it appears that the sent and received are based upon a player's perspective (it says as much in the docs). And in this case the "other" player would perform the action since less than say .5 seconds had elapsed. The owner values are slightly higher because (i assume) they entered the world first.

#

There is something about late joiners (not seemingly easy for me to test during development) where they can have a negative value for sent. Again I assume that it is computing a value based upon the player and the negative sent value tells us "you were not in the world" at the time.

#

Subtracting sent from received would in such a case yield a large positive value (I believe). While this wouldn't cause a problem if the cutoff is a second or two it could yield a false positive if the period was longer. So I'm checking for a negative "sent" and simply flagging it as not applicable.

#

Does all this seem to make sense**?** 🙂

stone badger
#

Oh just had a thought... if the send time is negative and that indicates that the player was not in the room I may not have to check anything else. The number of seconds (or fraction of course) between sent and received would typically be very short for anyone in the world. I can't even figure out what could cause it to be greater than some cut-off value.

harsh pebble
# hollow wagon is there any good explanation how to network/synchronise animations?

Implementation depends on what purpose is for the animation. There's a few different ways, but sadly I haven't found any all in one page you can go to that'll explain it.

For simple looping animations, you can go to the vrcprefabs database and find there's an animation sync script that will sync up the animation based on universal timecode of the players system clock. Pretty genius and uses 0 networking, but can differ slightly based on the system if clocks aren't set correctly.

The other way is to build flags and logic into OnDeserialization to trigger animations around the same time for everyone. You can also do that with SendCustomNetworkEvent, but events only fire once so not late join friendly.

The only other method I can think of is one I am just starting to try and work with, but using a simple master system that periodically samples the current playback time of an animation and adjust every other players animator to that if they deviate too far. You would also have to add the time it takes for the remote to deserialize and receive the new value. This would be done with OnDeserialization as well

raw umbra
#

If your looking for "realtively synced animations" you can use a simple network script to change a boolean or an enum to trigger the animation being enabled/disabled on the object

#

If your looking for "the animation happens at the precise same time for everyone", you will need a delay for the person triggering the animation, roughly 1/3-1/4th a second, then use server time to correlate all of the clients to trigger at the same time

#

I heavily recommend the former rrather than the latter

tulip sphinx
#

"all" for network events means "all but me", you need to also add normal SendCustomEvent. also thats not how you sync doors.

#

two doors on one animator setup, replace d1 event with Interact for one door

hoary frost
#

additionally, synced bools/onchange nodes do both for the client, for everyone else, and for late joiners. Ideal for such situations imo

cerulean zealot
#

question, do player ID's change if someone leaves?

#

or what can cause player ID ints to change?

lone zealot
# cerulean zealot question, do player ID's change if someone leaves?

"playerId" is a bad name in my opinion.
Basically its an incrementing per-session ID.
That means that the first player to join the world gets the ID 1, and then the second player gets 2, etc. even if the first player leaves and comes back they will just get the next increment and not the one they originally had.
Its not "bound" to a user/player, but it is unique, meaning that at no point will there be two players with the same ID in one instance.

cerulean zealot
lone zealot
#

But yeah a playerId will not change during a session

cerulean zealot
#

@lone zealot yeah I'm looking through my code and it's just not adding up.

Is there particular cases where a call for an udon custom event (non-networked) just...doesn't execute?

hoary frost
#

Im seeing some oddities at the moment, so question

Assuming this here is on the "Udonbehaviour 1"
Would this execute "Event 2" on the Owner of "Udonbehaviour 1" or "Udonbehaviour 2" ?

#

This should be on n1, right? Because the Isowner node checks for the owner of the script the node is on

tulip sphinx
#

yes

cerulean zealot
#

@hoary frost This would execute Event 2 on Udonbehavior2 locally for whoever the Owner is for Uddonbehavior 1.

If the public variable of Udonbehavior2 isn't pointed to anything, it will call the event, but nothing will happen. (it wont throw an udon exception)

strange gyro
#

You might need to provide the instance of the current gameobject to IsOwner, try using the Const This node

tulip sphinx
#

nah, empty stuff defaults to this already

neat atlas
#

Hello, i have a problem in my world currently with users with clients being able to mass teleport users.
is there a way to fix my udon script so that clients wont be able to do that ?

half epoch
neat atlas
half epoch
vapid pagoda
neat atlas
vapid pagoda
neat atlas
#

thanks so much for letting me know abt that 😭🙏🙏

empty plaza
#

So I have this where is you click a button only one row of avatars show while there others are off. when you go to click a different button for a different row the previous turns off and so on. This works for the three rows that I have but if I click the same button again it will turn on all that are hidden which is bad cause of lag. Is there anything I can do to fix it?

open ember
#

Okay so, message from my programmer

"There is recursion between onDeserialization() and ApplyToggle() due to all the buttons being connected to one another. Is there a fix to get bypass calling RequestSerialization() or to somehow detect whether RequestSerialization() is being called from a specific spot? If not than is there an alternate way to code a music system?"

I have no idea what any of that means, but we don't know how to fix this problem and wanted to see if anyone knows a way

strange gyro
#

Are your buttons calling Interact in an OnEnable/Disable event or something?

#

The only place RequestSerialization is being called is through Interact

#

When that happens and OnDeserialization is fired there's nothing else in that code path that calls RequestSerialization again unless there's some other component on one of those gameobjects being turned on or off that is triggering Interact or a RequestSerialization again

obtuse echo
#

RequestSerialization does not directly call OnDeserialization, so it's not possible that those are causing recursion, at least not directly. What can happen though, is that by enabling/disabling your button objects, you are somehow calling their OnDeserialization method with the wrong states.

I don't have enough information to know what the problem is, but here are some tips for networking stuff:

  • don't ever disable networked objects, it's just easier to reason about when you receive data and when you don't. I'm pretty sure OnDeserialization won't get triggered when the object is disabled, right?
  • isolate toggling into their own objects, so instead of doing go.SetActive on the objects of another button, you call button.ToggleObjects or something
open ember
#

We'll try these out when she's next avaliable. Thank you both!

stone badger
# open ember Okay so, message from my programmer "There is recursion between onDeserializati...

I have no idea what any of that means, but we don't know how to fix this problem and wanted to see if anyone knows a way When your code becomes too complex to follow it is an indication that you are doing something wrong. This particular example has many potential problems. Notice that your public book named global is red? This is a reserved word and should not be used as a property name. It might work but why risk it? What is "global" about it? Your arrays named turnOff and turnOn seem to contain objects that both turn on and off depending upon the state of State. There must be another name you can use that indicates what the collections are. There is only 1 thing different in the branch in Interact and that is a call to RequestSerialization, I wouldn't duplicate the other stuff. Importantly however is why would you "sometimes" not sync a UdonSynced property? Doesn't this just sound like a bad idea? Simplify your code until you recognize what it is doing at an instant.

harsh pebble
#

You know it's going to be a good day when you wake up and the first thing you see is a post from tom saying he has no clue what anything means 😂

stone badger
harsh pebble
#

Hey @frozen igloo sorry for the ping but I think you have some experience with this.. do you have any tips / examples / templates of a basic flow for syncing npc enemy state machines? I don't have trouble making local ai state machines but I've never practiced with syncing to a master, and any logic accounting for master change and ownership transfer.

hoary frost
hoary frost
#

but yeah as Puppet said, you shouldnt ever disable objects that have networking stuff in them, otherwise they might not recieve data when you need them to.
Thats why if I need to disable something, i make sure it doesnt have any logic inside of it itself, but on something related to it that i can easily link it to

#

i do wanna help you here, but i dont think i understand exactly what your programmer wants to do?
And yes there are definitely different ways to code a music system, although enabling/disabling gameobjects does work

#

Afaik there is no way to independently detect if requestserialization is called from a specific part of the script, but if thats what you want to do, you could use a logic gate there with a customevent or something, but again needlessly complicated when there are simpler ways

hoary frost
#

hm?

#

got pinged

empty plaza
#

Sorry was just confused what to swap out but i figured it out. my bad

elder girder
#

is it possible to get the type of the instance by code (group public, public, friends+, ...)?

round finch
#

Does anyone know why another player colliding with a player in a VRCStation causes the seated player to exit the station? And if that other player is close enough then they can't re-enter the station either, it teleports the player to the station but doesn't actually seat them. This issue happens when a rigidbody is attached to my object.

cedar crescent
#

I'm queuing up data to send using OnPreSerialization. Could another player take ownership after RequestSerialization but before OnPreSerialization is called? Do I need to reject ownership requests here?

strange gyro
#

Someone can take ownership up until OnPreSerialization so you'd have to handle ownership changes, after that it's about to serialize data so RequestSerialization calls shouldn't apply again until after serialization has completed and the next network update happens:

Take Ownership > RequestSerialization > Time passes until next network update (someone else could take ownership up until this point) > OnPreSerialization > Data is serialized and broadcast to other clients > OnPostSerialization > Time passes until next network update

#

I'd be surprised if ownership transfers could happen during the serialization phase

#

Think of the case where you have a button that 2 players could press and it doesn't sync the button state until 10 seconds later, during that 10 second interval both players could have pressed the button multiple times and transferred ownership back and forth numerous times before any data is synced

cedar crescent
#

Ok, that really helps! It just felt wrong calling SetOwner and editing the synced variables in different places. I think I'm getting it now

strange crane
lone zealot
strange crane
#

It works still.

lone zealot
#

I would still avoid it just in case. But again I might be misremembering or Unity fixed it. Just a heads up.

#

If you put it into its own namespace then I guess yeah it should be fine.

strange crane
#

Thats what I was about to say

hybrid kindle
#

whats goin on here consol isnt giving me much to work with

#

map can upload but cannot enter it in game

#

where can I find the network id

#

ah nvm found where it was

sage trail
#

open this menu and then hit regenerate scenes ID

#

I think that's the solution

hybrid kindle
#

yee that worked, not sure what casused it though

dreamy pike
#

What are the values that are being provided for DeserializationResult under OnDeserialization when it's triggered after player join? If I take the difference between result.receiveTime - result.sendTime when OnDeserialization is triggered for player join, I get a rather large value in the order of 10s of seconds (like ~60s). Are these values undefined for player join?

stone badger
#

I'm mostly interested in a formula that can be used to "expire" the message so late joiners sync the value but do not run the processes associated.

wintry wharf
#

Hello can someone explain the networking to me i just want to spawn an object which everyone can see it right now its just local the object hast the component vrcsynced and its on continues also i set the owner to the person which press the button which spawns the object what am i doing wrong?
i'm working with c# not with the graph

public class UITestButton: UdonSharpBehaviour
{

    [SerializeField] private GameObject spawnObject;
    [SerializeField] private Vector3 spawnPoint;
    [SerializeField] private Transform parent;

    public void Spawn()
    {
        SendCustomNetworkEvent(NetworkEventTarget.All, nameof(SpawnObject));
    }
    public void SpawnObject()
    {
        GameObject go = Instantiate(spawnObject, spawnPoint, Quaternion.identity);
        Networking.SetOwner(Networking.LocalPlayer, go);
    }

}
pseudo mortar
#

Hey @tulip sphinx, i know you helped me out a bit ago and i was wondering if you could help me again. So using the video you linked previously on how to do proper udon networking i attempted to make an udon graph that toggles an array of objects like i had previously been trying to do. This time it works for the most part. The only issue is that for each button in the world it must be clicked twice before it starts working. Is there any way to fix this? (to be clear after one person has clicked said button twice everyone else only needs to click it once and it doesnt matter who presses the button from what i can tell) When someone clicks the button at first nothing will happen then on the second click objects will be toggled. Sorry if im bugging you at all

tulip sphinx
#

@pseudo mortar if objects are enabled initially, set default value for synced variable to true

pseudo mortar
#

with some of the objects being toggled being off by default and some being on by default

tulip sphinx
#

check state of first one against synced variable. if equals, do nothing. if not equal, for array: get object state - negation - set state

pseudo mortar
#

got it, thankyou

wintry wharf
#

@tulip sphinx sorry to bothering you but can you maybe help me out too?

humble girder
wintry wharf
wintry wharf
# humble girder Instanciated object can't be network. You have to use VRCObjectPool to pre-popul...
public void Spawn()
{
    GameObject ob = pool.TryToSpawn();
    if (ob == null) return;
    ob.transform.SetPositionAndRotation(spawnPosition, spawnDirection);
}

can u give me maybe an example or explain why i still cant see it when someone else is pressing the button and over the pool the object spawns?
maybe the problem is on the gameobject itself i dont know vrc object sync component is on it do i need something else?

humble girder
wintry wharf
#

public void SetOwner(VRCPlayerApi player)
{
Networking.SetOwner(player, gameObject);
}

#
public void Spawn()
        {
            GameObject ob = pool.TryToSpawn();
            if (ob == null) return;
            ob.transform.SetPositionAndRotation(spawnPosition, spawnDirection);
            Networking.SetOwner(Networking.LocalPlayer, ob);
            Networking.SetOwner(Networking.LocalPlayer, gameObject);
        }

wait do i need to do this here something like that?

humble girder
pseudo mortar
strange crane
#

What can to fail serialization. I have a local ui sending data to a synced script.

tulip sphinx
#

@pseudo mortar uuuh, along with setstate you have getstate. so if getstate of first item in array is not equal to synced bool you: get - negate - set state of every object im array

pseudo mortar
# tulip sphinx <@779868286858690620> uuuh, along with setstate you have getstate. so if getstat...

i believe i know how to do most of what your saying via getting the bool value of the current item then using unary negation to do the opposite value but im unsure what you mean by getting it to see if its not eqal to the synced bool.

With the current screenshot being what i have rn in an attempt to follow what your saying it produces the affect seen in the following two videos where if the object is off when someone joins it syncs but when someone joins and the object has been toggled back to be on then it desyncs. I imagine the part of your explanation im not understanding will solve this but i thought i should include it just for reference.

tulip sphinx
#

if getstate of objects[0] is equal to synced bool, do nothing. otherwise do your stuff

#

"apply" gets called on deserialization (and locally)

pseudo mortar
tulip sphinx
#

huh

#

branch

#

and equality

pseudo mortar
# tulip sphinx and equality

ok so the thing in my image doesnt work exactly so where did i go wrong? and where would i put the equality node in?

tulip sphinx
#

uuuuuh. event - branch (items - get item 0 - get active - equals synced bool) - false - loop

#

somehow i dont see synced bool in your stuff anymore. but it was there

pseudo mortar
tulip sphinx
#

uh, false

#

true would work but only if object 0 is initially opposite of synced variable

#

wait noo

#

you get active self and then compare to synced bool

#

and the result send to branch

pseudo mortar
tulip sphinx
#

yes, looks fine

pseudo mortar
# tulip sphinx yes, looks fine

got it, and by compare you mean use the equality node and put the values of get active self and my state bool into it then the bool value of equality into the branch node??

#

like this?

tulip sphinx
#

probly ye, i honestly dont remember how they called

#

ye looks fine

#

once, agsin, branch - false

#

or youd get double click agsin

pseudo mortar
#

one second let me test

#

ok so i tested and it of course toggles the object back and forth and i only need to click the button once but it doesnt sync for some reason.

#

like it wont sync for late joiners and it wont sync for those in world

tulip sphinx
#

state - synced

pseudo mortar
#

ah oops

#

ok well ignoring that last thing i said there is a small thing where if one object is by default off you have to click it twice and then once you click it the second time the object thats off will be toggled on and the object that is on will just stay on and then from there itll toggle both of them. But if all objects are on by default it acts fine. Im assuming this is because its only looking at the first object in the array with the get gameobject array node that is before the branch node. Would adding a for loop fix this @tulip sphinx?

#

i just realized that wouldnt exactly work, so what should i do exactly?

tulip sphinx
#

oh i see

#

in loop

#

object(index) - getstate - unary negation - (same object) set state

#

so replace "state" variable with state of thay exact object in loop

frank fjord
#

When scripting in continuous-sync mode, what changes with how and when values arrive, and when network functions are called? Since continuous sync interpolates values, I presume the value of a synced variable can change even beyond frames where OnDeserialization is called... so how can you efficiently tell when or when not to perform logic or change the state of something? Basically determining when the script should 'sleep.'

Also, does network activity on a continuous-synced script stop when the synced variable isn't changing? Or do continuous-synced objects spam the network at all times?

#

... I don't really WANT to use continuous sync, but the alternative is some very complicated shenanigans juggling VRCPickup/VRCObjectSync stuff and it'd be nice to not have to do that.

tulip sphinx
#

they do spam at 5hz

#

deserialization is not a thing for continuous afaik

frank fjord
#

even when their value isn't changing??

#

God. How do worlds with more than like 3 synced pickups not have network suffering at all times???

#

Something doesn't check out

tulip sphinx
#

my tests show that 200 is more or less the limit, i ended up with 150+anything other than vrc sync ie transform values is on manual sync

pseudo mortar
#

ok @tulip sphinx so it works almost perfectly but the one issue is that if the first object in the array (object 0) is off by default you have to click it twice. But if the first object in the array is on by default it works just fine no issues

tulip sphinx
#

then make synced State var public and set it true of false manually for each instance

#

or just make sure that first object in array is always enabled initially

pseudo mortar
tulip sphinx
#

or if you want to do it properly then add another bool var (not synced, default is true) and check against it, not state of 1st object. and after loop set that variable state to negated as well.

pseudo mortar
pseudo mortar
#

well funny enough it turns out i do need to do it. I have little capture point things throughout the world (similar to what youd see in the star wars battlefront games) and i have them set up as buttons so to say so when you click them theyll toggle things around to change it from one side to the other. Well i also have little admin buttons which do the exact same thing from the spawn area so that way you dont have to go around the map to toggle them if needed. So in order to prevent the double click thing happening on those specifically i need to do what you were saying

essentially i have 2 buttons in different places in the map that do the same thing

pseudo mortar
# tulip sphinx or if you want to do it properly then add another bool var (not synced, default ...

for this i am slightly confused as to where you are suggesting i put the other bool variable

for reference the regular bool variable (state) is set to be synced and to be defaulted at true

and the second bool (OtherState) i just added is not synced and its value is also by default set to true.

So in the image below i have it set up how i think your saying it should be but now i am unsure what else you are saying to do. Sorry for bugging you again hopefully this will be the last thing.

pseudo mortar
#

nvm got it working

frank fjord
#

When you call Networking.SetOwner(), how does - or doesn't - this interact with network serialization? Does it...

  1. automatically flag the object as network-dirty, like RequestSerialization() does?
  2. send its own smaller, independent serialization event over the network immediately/at some point to resolve the ownership change?
  3. not do anything at all, and a manual RequestSerialization() call is needed for ownership to try and change?
stone badger
# frank fjord When you call Networking.SetOwner(), how does - or doesn't - this interact with ...

kind of a 4) I think. What it does behind the scenes I don't know for certain but it establishes the player as the authority. They can set values and upon calling RequestSerialization the values it set will be transmitted. If the owner wasn't set it wouldn't transmit them as the current owner is the authority. It absolutely doesn't do 2 as the values should be identical and if they aren't they are about to be changed in any case.

frank fjord
#

The nitty gritty here is important because players can enter race conditions where they fight over authority, and especially in my case, I need to execute logic (or stop logic) based on authority.

#

I know, also, that there are some ways you can 'refuse' an ownership transfer

#

but that takes time

#

If you locally set yourself as owner, you're the owner until someone tells you "No, actually, you couldn't do that"

#

But in those instances... what happens in the interrim, and after?

stone badger
#

I don't believe there can be a race condition as the server handles the processing. It most likely has something in place. You can only be refused ownership if you "ask" rather than set it.

frank fjord
#

The server doesn't handle anything

#

There is effectively no server.

#

Just a relay for messages between players.

#

AFAIK, the proton servers handle like VRChat authentication and message repeating and that's it. There is no networking 'authority' happening up there, right?...

stone badger
#

Well not "server" per se then. But the acting "server" being the entity responsible for completing the operation. And of course when a player leaves a bunch of sync calls go out establishing who is the owner now.

#

I don't know the details of course.

frank fjord
#

Okay, so. OnOwnershipRequest runs both on the requester and the owner... :S

#

That may be helpful but it complicates things a lot I feel.

#

It's triggered, I presume, by the requester calling SetOwner()

#

There are no alternatives, no "RequestSetOwner" for example

#

God. I swear, I feel like I had a conversation with someone about this like a year ago and I've lost it entirely. There is a specific order of operations here with SetOwner(), OnOwnershipRequest(), and OnOwnershipTransferred() that I don't recall...

normal sonnet
#

For live music events, where the video source is a live stream like VRCDN... how important is it to code udon to make sure that everyone is "together" and watching the same moment?

Wouldn't it be impossible to be behind or ahead of a live stream?

wooden sentinel
solar widget
normal sonnet
solar widget
hoary frost
faint rock
faint rock
faint rock
normal sonnet
faint rock
#

The resync in ProTV likely reads the current position from the video owner, pauses, changes position then unpauses.

hoary frost
#

and i am trying to run it for both quest and pc in a performant manner, its for diverse background music in a game world

faint rock
solar widget
hoary frost
faint rock
hoary frost
faint rock
#

The Unity VRC player is essentially what everything else is built on top of, extending the functionality.

hoary frost
faint rock
hoary frost
faint rock
#

AVPro supports audio-only but not sure VRC supports requesting audio-only from YouTube if you're pulling from there. If you're hosting the content yourself then you can load pure audio files and it'll work, it's the Unity player that doesn't support audio-only files.

hoary frost
#

yeah im pulling from youtube

faint rock
#

Video decoding seems to add a notable overhead for some reason, so I'd say yes if you're going for optimising, also lower network bandwidth usage.

#

Ah, in that case won't matter as you can't do audio only from Youtube, VRC doesn't handle passing arguments to yt-dlp to do it.

#

(it CAN, but it's not implemented, as I know Resonite does it)

hoary frost
faint rock
#

That would disable the rendering aspect, but it would still be loading and decoding the video.

hoary frost
meager meadow
#

I am jumping in the middle, but what I do is create a video with a static image and the audio I want. That is very easy to decode. You can use ffmpeg to mux the audio with the static image for the video.

faint rock
faint rock
meager meadow
#

Interesting. I was able to play audio-only files via AVPro in Altspace, but haven't tried that in VRC. Does that work here?

hoary frost
faint rock
solar widget
faint rock
solar widget
faint rock
solar widget
coral ingot
#

how would i add a custom vsarable to the player object like health?

raw umbra
#

As long as the player whose object it is controls it, it should prevent odd issues like crossfire or desync, I recommend sending damage to that player via some sort of damage management object (So perhaps 2 objects per player)

runic gate
#

how do i network instantiation via udon sharp

random parrot
runic gate
#

im trying to spawn a drink prefab on an event

random parrot
#

it sounds like you'd be better of using an object pool

runic gate
#

ill check it out

runic gate
#

thx

#

public VRCObjectPool ObjectPool; is the correct variable for setting an object pool right?

runic gate
#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class recipeye : UdonSharpBehaviour
{
    //ignore these variables there controlled by a seperate script
    public int milkCount;
    public int caffeineCount;
    public int syrupCount;
    public int coffeeBeansCount;
    [Header("Recipe")]
    public int recipeMilkCount;
    public int recipeCaffeineCount;
    public int recipeSyrupCount;
    public int recipeCoffeeBeansCount;
    public VRC.SDK3.Components.VRCObjectPool ObjectPool;
    public GameObject Drink;
    public Transform SpawnLocation;

    private void OnTriggerEnter(Collider other)
    {
        MakeCoffee();
    }

    public void MakeCoffeeYe()
    {
        MakeCoffee();
    }

    public void MakeCoffee()
    {
        // Check if all required ingredients match the recipe exactly
        bool isMilkCorrect = milkCount == recipeMilkCount;
        bool isCaffeineCorrect = caffeineCount == recipeCaffeineCount;
        bool isSyrupCorrect = syrupCount == recipeSyrupCount;
        bool isCoffeeBeansCorrect = coffeeBeansCount == recipeCoffeeBeansCount;

        // Verify all conditions
        if (isMilkCorrect && isCaffeineCorrect && isSyrupCorrect && isCoffeeBeansCorrect)
        {
            InstantiateDrink();
        }
    }

    private void InstantiateDrink()
    {
        // Instantiate the drink object and set its position
        milkCount = 0;
        caffeineCount = 0;
        syrupCount = 0;
        coffeeBeansCount = 0;
        GameObject drinkInstance = VRCInstantiate(Drink);
        drinkInstance.transform.position = SpawnLocation.position;

        // Optionally, initialize the drink with recipe details here
        // For example, you can set the quantities or any other relevant data
    }

}
``` i really need help networking this
vapid pagoda
# runic gate ``` using UdonSharp; using UnityEngine; using VRC.SDKBase; using VRC.Udon; publ...

you cannot instantiate networked objects (unless you create your own syncing solution for that)

every networked objects has an id assigned to it, which cant be assigned at runtime, only in the editor

the idea of an object pool is to have a lot of objects already stored and hidden, and then shown when you need them

https://creators.vrchat.com/worlds/udon/networking/network-components/#vrc-object-pool
https://udonsharp.docs.vrchat.com/vrchat-api/#vrcobjectpool

This doc covers Networking Components, Properties and Events you can use in your Udon Programs.

runic gate
#

alr

runic gate
#

this really sucks since if we were able to use photon which i dont think vrc uses photon we could have just done photonnetwork.instantiate();

frank fjord
#

Man. I am struggling a lot with networked object ownership.

I am writing a control scheme for a vehicle that includes a number of interactable objects. These objects need to be owned by the same player at the same time, and so long as a player is actively operating the vehicle with them, none of those controls - which are all separate objects and scripts - should be 'stealable' via other players.

This is not an easy task. Ownership race conditions are so easy to produce, and the order of operations in which network events and ownership changes take place is so opaque I cannot figure out how to safeguard the system against lockups. I'm on like week 2 of just trying to make a two-handed wheel and throttle not softlock if two people grab it at the same time.

#

The following seems to be possible and it's REALLY painful to resolve:

  1. Player A grabs a control, proclaims ownership of it, and sets various Udon Synced variables on it.
  2. Player A holds this ownership long enough to successfully serialize an outgoing message with those synced variables in it.
  3. Player B manages to locally take ownership of the same control during this timeframe, having not received notice that Player A already owns it.
  4. Player B changing the ownership of the control, itself, does not inherently serialize the synced variables.
  5. Player A gets their ownership revoked, but because Player B taking over ownership itself doesn't send a variable serialization, Player A still sees the synced variables as they set them, during the window of time where they had ownership.
  6. Player B took ownership before receiving Player A's variable serialization, and so they seem to have disregarded Player A's message. The two players now are in different universes.
obtuse echo
# frank fjord The following seems to be possible and it's REALLY painful to resolve: 1) Playe...

To point 6: That should not be happening. Both, ownership and variables, should resolve automatically when two clients interact with the object at the same time. I assume you are doing more than just setting ownership and request to serialize a new value? Maybe it could be related to you having multiple objects, but I feel like they would take ownership at the same time anyways, at least if you set the owner on the same tick. Alternatively, you could just take the main object and check if the owner is the same before you take any network actions. Oh, and last thing, do you disable some of those objects? In the past at least, I always noticed that some values are not synced properly, so I just keep network objects enabled as much as I can.

vapid pagoda
normal sonnet
#

This should be ok code for a button to set ownership of another object to the local player right?

I know playerapi holds a lot of data in it, but it seems like that's the type of info setowner wants

stone badger
meager meadow
# frank fjord Man. I am struggling a **lot** with networked object ownership. I am writing a ...

The default ownership philosophy seems to be to allow things to get out of sync, but eventually let one of them prevail so eventually sync them. Since player B doesn't modify the sync'd variables in your example, you may have run across a situation where that doesn't work. You might try adding a variable to modify to force a sync, if you can rely on "eventually".
Another thing to look at is the more complex ownership transfer system that provides for rejecting an ownership transfer: https://creators.vrchat.com/worlds/udon/networking/#requesting-ownership-advanced. That's not a panacea either, though. If you look at the diagram, there is a period of time "Orange" thinks he owns the object before receiving the rejection from "Blue". Also, there is no positive confirmation to "Orange" that he was allowed to take ownership, so he never really know if the transfer was successful. He needs to be able to "unroll" the state if he receives a rejection after his request.

Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.

stone badger
# meager meadow The default ownership philosophy seems to be to allow things to get out of sync,...

I've alluded before to the system I developed and now use exclusively and I won't go back at this point. My game objects don't sync at all and are all SyncMode.None. They all have a "sync" object which can support multiple game objects when needed. This means that local and remote objects are all syncable, almost by default. I recommend that the UdonSync properties be placed on such a sync object and that other game objects just reference them.

#

If we use a simple example of a sphere and a UI control toggle. Neither of them are syncable but both reference a "sync object" that contains the state is takes care of the networking without my having to think about it. The toggle sets the sync object, the sync object tells the local sphere (and remote instances) that the value changed.

patent robin
# stone badger I've alluded before to the system I developed and now use exclusively and I won'...

Hey that's similar to what I've done for my synced UIs ! It's inspired by what we do at work (which is itself inspired by how React works).
Basically the UI elements are SyncMode.None but they update a parent object which is SyncMode.Manual and stores the state of the whole panel in a DataDictionary. If the local player has authorization to update the object, it will update the state from the UI elements and - detecting that change - the parent object will update its synced variables and call RequestSerialization(). But if the local player doesn't have authorization, the UI element will be refreshed from the state

#

(but when you have hundreds of synced vars for different objects I wouldn't recommend keeping them on a single object, especially if some of them update more frequently than others)

#

Anyway a small thing I have for my UI that could interest you @frank fjord is that the panels use a "lock source", an empty game object + script with a single Locked() method that says if the local player can modify that UI. I can give the same reference to multiple panels so that they all lock simultaneously, for example if a player takes ownership of one of them :)
Practical example - in my world I have a lever that locks the 3 generator panels for everyone except for the person that activated it, and it's that way until that person unlocks it or leaves. The lever activates the lock source, and the panels read from it when someone tries to change a value (in practice it also shows a "Locked" popup)

stone badger
#

It is good to have a system rather than a collection of inconsistent, ad-hoc, one-up solutions. I tend to consider the game object I call an "agent" to be the definer of the state of something. The sync object is a communication convention and they contain a single property. If a sphere can toggle between visible (or not) and a button can toggle it then the SphereAgent is the authority. The sync object gets its initial state from the SphereAgent which knows if it is visible or not at start up. The button (let's say it is a modal button) checks the state of the sync object to see if it should appear pressed or not.

frank fjord
frank fjord
frank fjord
# patent robin Anyway a small thing I have for my UI that could interest you <@2685991112787886...

I do have methodologies for checking to make sure that it's legal to take ownership in the first place. In my case, if a player is currently holding any of the piloting controls (and other players KNOW that they're being held), other players do not have the ability to take over those controls.

The problem is what happens when other players DON'T yet know that the controls are being held, because data hasn't yet been received.

frank fjord
# meager meadow The default ownership philosophy seems to be to allow things to get out of sync,...

On a further note, I'm even MORE afraid of using this ownership request flow because it introduces a window in time where the requesting player could get an ACTUAL DATA SERIALIZATION while waiting for an ownership request reply, BEFORE that ownership request reply arrives. This would be super bad and make it even harder to "undo" the actions the requesting player tried to take in the interrim (and resolve synchronization between them and the original owner)

meager meadow
# frank fjord On a further note, I'm even MORE afraid of using this ownership request flow bec...

Without that method, when a player requests ownership, a message is sent to the other players (not sure when) saying the requestor is the owner and, locally, you are considered the owner. There is no facility for rejecting the request. That's likely what you are running into -- if two players request at the same time (or close to it), they both think they are the owner and continue on. Things "eventually" resolve themselves are ownership is transferred at the server and a serialization request is sent to everyone. It's not a synchronized system at all.

#

@stone badger I'm thinking the same thing as Luzak. I don't see how that resolves the issue. I may be missing something...

stone badger
frank fjord
#

Let me try to lay out what I'm actually doing

stone badger
meager meadow
#

It shows up mostly in my "world editor", where I allow anyone to edit any object.

stone badger
frank fjord
#

I have a piloting control deck with 3 grabbable controls: a wheel, throttle, and altitude control.

Players start by grabbing a local, non-synced pickup. This pickup script informs its "manager" script that it was picked up or dropped.

The manager script querries a script that manages the whole control deck, and asks if it's legal to take control, based on if the player does or doesn't already own all the control objects, and if any of the control objects are known to be grabbed by another player. If it's legal, the control deck returns "true" to the pickup manager.

The pickup manager becomes owned by the grabbing player, and synchronizes a one-time message with the player's position and rotation offset from their hand bone to the grab point, what hand it is, etc, and replicates that to other players.

Other players, receiving this message, can now locally - without continuous network activity - simulate the movement of the grabbed controls by just applying the sent offsets to the owning player's hand bone.

Eventually, the grabbing player drops the control, and sends out another network message saying the pickup was dropped.

meager meadow
# stone badger How does it manifest itself? I have player selectable buttons (for instance) I d...

In your example, it would affect a single object, the sphere. If two people would press at about the same time, it likely wouldn't be noticed. Assuming it was off, (1) if the race condition occurred it would be turned on so everyone who pressed the button would say "it worked", and (2) if the race condition didn't occur, people would see it go on then off, then have two people saying "I clicked the button" so the toggle would be expected.

frank fjord
#

Currently, I'm using my own sanity check ("Are any of the other controls grabbed by a player that isn't you?") to locally reject ownership transfer. In a race condition, the player grabbing "first" loses when the second player - thinking that it's safe to take ownership - steals it away in that split second gap.

Alternatively, I could implement VRChat's "request ownership" logic, but that isn't infallible either. All it does is require more function calls, and changes it so that the player grabbing "second" loses.

meager meadow
#

Remember, it "eventually" resolves itself with one player's view overwriting all others. That's how it eventually synchronizes. But if the action isn't a simple state change, things don't work.

frank fjord
#

In either case, a player thinks they grabbed a control, and later finds out they didn't, and needs to resolve the state of the universe such that everything works and is in sync again, and this is not trivial. It's especially not trivial when I am deliberately not sending serializations frequently. It's the whole reason I made this system to begin with, is so that I don't need to spam the network with updates.

#

I'm also trying to keep the controls decoupled. I don't want to send info about all of the grabbable points on the control system, every time a single one of them is grabbed or released.

#

So what happens if I try to grab the wheel, but you try to grab the throttle? First I take ownership of everything - but I'm only sending a serialization about the wheel - and then you inform me that no, actually, YOU'VE taken ownership of everything - but only send me a serialization about the throttle.

meager meadow
#

That's very similar to my world editor issue. Unfortunately, I haven't found a great solution. In my case, I was able to mitigate it somewhat by making the controls local and synchronizing the effect, if that makes sense. The effect in my case was changing the transform (including scale).

frank fjord
#

I actually am synchronizing the effect elsewhere, in either case: the piloting of the ship

#

but the visible movement of the controls I want to be smooth and not update at 5hz (and not update out of sync with remote players' hands)

meager meadow
#

Yeah, I wrote a smoother too. Deserialization changes a "target" transform, and locally each player LERPs the transform to the new target.

stone badger
meager meadow
#

The smoother may now be ideal in your case, though, @frank fjord , since it sounds like you have avatars in a vehicle. Their position wouldn't necessarily be kept with the vehicle.

stone badger
frank fjord
#

The vehicle is irrelevant. The game features a single shared vehicle that all players inhabit, and it does not 'truly' move (the world moves around it)

#

There are two different things happening. The broken system is the system that smoothly displays the rotation and movement of the grabbable controls themselves, and coordinates when and where they can be grabbed by other players.

The second part is how the controls actually pilot the ship (or the world around the ship), and this I haven't even gotten to implementing yet 'cuz the controls keep breaking in different ways lmfao

#

I spent two weeks in an excel sheet simulating out different extrapolation and correction equations and now I'm being blocked by a steering wheel lmfao

#

... but I'm gonna have to solve this eventually. Every single interactive element in this game world is going to be one of these diegetic, grabbable controls. These need to work perfectly.

#

This is also why I'm not just using a dumb VRCObjectSync on a pickup. It's not performant or smooth enough

stone badger
#

It may likely be I'm not doing anything in the world that is negatively affected by an occasional dropped update. I have a flight system and they consist of several parts that interact. All players see the same effects and only the person holding it can affect a change. If they drop it, pick up by someone else is possible and it cleans up if the player leaves while holding one.

frank fjord
meager meadow
stone badger
#

I'd naively believe that a similar system could assign the control or the deck to whomever grabbed it. Isn't it theirs tuntil they let it go?

frank fjord
#

That's what's going on, yes. If I grab any of the controls, that control asks for ownership permission from the entire control deck. The control deck checks to make sure that no other player has ownership AND is grabbing any one of the controls. If it's clear, the control deck and all of the controls get assigned to the grabbing player, and that player SHOULD be the ONLY player able to manipulate any of the controls until such a time as they let go of all of them

#

... but of course there is this race condition

#

where two players both try to grab a control at the same time

stone badger
#

I use VRCObjectSync for the physical properties but not for what color the stick (and the nib) are when it is being used. A player can hold one in each hand and they are similarly sync'd.

meager meadow
#

And to be fair, the ownership transfer working like this is what allows people to request ownership then immediately start using it. If they made it synchronous, we'd have to set up callbacks for when owner transfer was successfully processed and/or rejected.

frank fjord
#

Like. I understand the basic premise. If you think you can take ownership, you do; if later you turn out to be wrong, you roll back.

#

Just, the order of operations of when network messages of different types are arriving, how things do or don't get synced...

stone badger
frank fjord
#

..... and the fact that there are multiple interconnected objects that all need ownership synced...

#

is making the real implementation of this tricky and fraught with bugs

stone badger
#

I think that might be where the difference lies. The parts of my flight stick are integrated. The "stick" communicates locally with the other parts, the sync values are centralized.

frank fjord
#

Yeah. Like I said, the control deck is complicated 'cuz there are multiple controls, and each control wants to send as little data as possible - just one update to say it's been grabbed (and what its offsets are), and one update to say it's been dropped - I don't want to send data for ALL the controls on the deck, every time a single one is grabbed or released.

stone badger
#

In another example I have a few service-based controls. They can be used by anyone but need to be serialized and due to the limits of how often file loads can occur I've set it up so the UI disables when the first player is processing it and it only enables again when the service has returned and the time between calls has been met.

stone badger
#

My BaseSync class adds support for expired messages (that's always been an issue for me) as we are stuck using sync'd values and in many cases don't want late joiners to act on them.

frank fjord
#

Okay, this is stupid. I'm baffled. What call does OnOwnershipRequest() actually return TO...?

#

Like, there is no Networking.RequestOwnership() function

#

and SetOwner() does not magically start returning a value

#

Is there no dev-accessible output for OnOwnershipRequest()? No way to make the return result of OnOwnershipRequest() actually actionable...?

patent robin
#

OnOnwershipRequest is supposed to be overridden so you can control how ownership is transferred.
There's a graph on the page I linked that explains the order of calls, but basically OnOnwershipRequest will be called first on the player requesting ownership then on the current owner, and in both cases returning true allows ownership transfer and returning false stops it

frank fjord
#

Yeah I see that. But the value the function returns doesn't go anywhere accessible to scripting

patent robin
#

Yes, but you could set a variable from inside the method and use it later (like use it in OnOwnershipTransferred)

#

Basically, OnOwnershipRequest always returns true unless you override it, and if you do there's nothing stopping you from storing the return value in a separate variable before returning it

meager meadow
# frank fjord Yeah I see that. But the value the function returns doesn't go anywhere accessib...

You are right, the true/false return value isn't available to scripts. The result just impacts what OnOwnershipTransferred() messages are sent to others. And unfortunately, a "true" message response in the final call is never sent to the requester, so there is no way the requester can know if it is actually a successful transfer. It has to just assume it was successful and adjust if it later finds out it wasn't.

frank fjord
#

I also ran some tests last night and learned, rather frustratingly, that when ownership is transferred, a serialization is NOT sent

#

I had a system of two pickups: when either one is grabbed, they grant ownership of both pickups to the grabber, as a method of locking out other players from intervening while the whole system is in use.

#

However, there is this small window of time where neither player has been told that the other player has tried to grab one of the two pickups

#

Player A grabs pickup A and sets a value on pickup A; player B grabs pickup B and sets a value on pickup B. Player A learns late that they lost the ownership race, but doesn't actually get a serialization informing them that the value on pickup A needs to get reverted... and they don't own the object anymore, so they can't do it themselves.

frank fjord
#

By the way. Does anyone recall off-hand...

  1. what the 'overhead' size of a serialization is? I seem to recall it's like 32 or 64 bytes.
  2. is that overhead per object being serialized, or per serialization event, total?
  3. what is the size of an ownership acquisition? since - as above - we've established that ownership transfer does not go through the same chain of events and doesn't automatically run OnPreSerialization or OnDeserialization, I'm not sure it's possible to easily scout out how much data it takes to take ownership of something on its own...
  4. ... and if this ownership acquisition, if it happens at the same time as an actual manual serialization, costs the same, or more, data to do...
plush tiger
#

Ownership is not serialization, it is just which client is responsible for updating the serializable data.
OnOwnershipTransfered will tell you when ownership of an item has changed
You have built a race condition, solving it will not be trivial. You will need to detect when the race has occurred and have rules for how to solve the race. Networking.GetServerTimeInMilliseconds may be somewhat useful in resolving the dispute.
With the latency of VRC networking it will not be a small amount of time this race can occur, I have seen roundtrip delays on the order of 1000ms before. People with poor internet and on an instance continents away from the instance vrc server. (EU Client in a US-West instance)

frank fjord
#

Changing ownership requires SOME amount of data to be sent. Even if it's not the same 'kind' of data serialization as calling RequestSerialization(), at the end of the day it costs some amount of data to tell another client that you're the owner of an object now

coral ingot
#

is it possible to network Instantiate or do you have to use pools? I thought someone said there was a way to set a network id in game

vapid pagoda
ivory sandal
#

i have a very fast moving object i need to sync almost exactly in order for my game to look normal. anyone know how to update the location every few frames? ive tried the object sync and its too slow

tulip sphinx
#

how can you expect to sync smth exactly when theres 200-700ms ping on average. unless it doesnt react to players. overall you probably make all simulation local and just send changes caused by players with deserialization, it should be faster than 5hz sync. but if you want to make a two player table tennis, no.

coral ingot
vapid pagoda
#

you will need to store an array of objects, how you sync them, depends on your implementation, personally, im using several udonsynced ulong array with lots of bit shifting to store and retrieved all my information, which is then serialized as late joiners appear

something more simple would be to have an array with vectors and quaternions and constantly serializing them, similar to vrc object sync, you wouldnt need to worry too much about late joiner syncing in that case but bandwidth would be a lot heavier

#

you will probably also need an array of gameobjects, each including some base script that can access the object spawners and all required information, and derive into new scripts as needed

coral ingot
strange crane
#

Does anybody have code for a hud like quickdraw or price guess where its a little delayed and lock in a position but rotates.

frank fjord
# strange crane Does anybody have code for a hud like quickdraw or price guess where its a littl...

One way to achieve this would be to calculate the difference in position and the difference in rotation from the current HUD transform to its target point, whatever you want that target point to be. Multiply that difference by some percentage of your choice between 0% and 100% - the higher, the snappier; the lower, the more languid and delayed - and then add the result to the current position and rotation.

#

Since the target position and rotation are based on the player's current position and rotation (their head IK, likely), make sure to do this in PostLateUpdate.

frank fjord
#

Hm.

So, I know that normally, when you request serialization on an object, ALL of its variables get serialized, whether they've changed or not.

I also know that in some cases, if you're careful, you can use UdonSync byte arrays to represent the data you want to serialize, and dynamically control when it gets sent, since byte arrays only use as much bandwidth in a given serialization as their current size requires.

I seem to recall there is some advanced 'gotcha' about doing this, though, specifically in regards to what happens with serialization if a byte array's current size is 0. I don't recall what the problem is though?

strange gyro
#

Synced arrays can be empty but shouldn't be null

#

And they have some extra bandwidth overhead

frank fjord
strange gyro
#

Nah there's some extra header metadata sent to handle things like the length of the array

frank fjord
#

gotcha.

#

I think one of my mini-tasks for today is gonna be getting a better grasp of how big serialization overhead is (nominally) anyways, so I'll just tack this onto my list of things to benchmark.

strange gyro
#

Someone posted a talk video a while back with more in depth info on the networking implementation

frank fjord
#

If you have any idea where that is, I'd love a link to that.

strange gyro
# frank fjord If you have any idea where that is, I'd love a link to that.

Link to the slides can be found here: https://docs.google.com/presentation/d/1fiEKP0a46O5CrgkehH5gNK6kFvgQtGIa4MIG_UcFmAg/edit?usp=sharing

At around 18:52, you may notice a section missing for ~2 seconds. All I was saying there is that if people tried to make recursive functions, they might not have had them work properly, and that the attribut...

▶ Play video
frank fjord
#

Ahh, so THAT was this Furality panel

#

Thanks!

strange gyro
#

It has a lot of great info, the whole video is worth watching

frank fjord
#

oh christ, the serialization header for an array is FIFTY TWO BYTES?

#

pain...

#

With barely 4 to 5 kb/s to work with (once factoring out the bandwidth players already consume from their 11 kb/s allotment for their own IK, VoIP, and animators), it's a wonder you can synchronize anything sometimes.

strange crane
frank fjord
# strange crane How would I calculate the difference between a Vector3 and Quaternion?

If you're asking how to calculate the difference between two Vector3s, AND how to calculate the difference between two Quaternions: I can tell you offhand that Vector3s can be subtracted from one another very easily. I don't remember precisely how to do quaternion math, but I'm sure Google would be happy to help you with that ❤️ (I'm sure the mathf library has some kind of 'rotate from/to' function)

If you're asking how to calculate the difference between, specifically, a Vector3 FROM a Quaternion, or vice-a-versa, you don't, and wouldn't want to.

strange crane
frank fjord
#

"calculate the difference in position and the difference in rotation from the current HUD transform to its target point,"

#

Your hud has a certain position, and a certain rotation, last frame

#

you want to move it to a new position, and a new rotation, this frame

#

there is both a difference between the last position and new position, and a second different difference between the last rotation and the new rotation

#

These are two separate differences: a positional difference, and a rotational difference.

strange crane
#

So I am caculating the diffrience between the previous and new pos and same for rotation

frank fjord
#

... yes 🙂

#

There may be a faster way to do this, actually.

#

There is a function called LERP, or "linear interpolation."

#

It takes three parameters: an initial value, a 'target' value, and a value 't' which represents a percentage (0 to 1.0)

#

If you pass LERP the values 2, 4, and 0.5, it will return the value that is 50% "between" 2 and 4 (which, in this example, would be 3)

#

This is just a faster way to do the thing I already explained.

#

For position, you would set your HUD's new position to the result of a Vector3 LERP from last frame's position to your desired target point, and for 't' you would give it that percentage from 0 to 1 to control how quickly it will try and move to the target point. The closer to 1, the faster it moves.

#

For rotation, you would ALMOST do the same thing, except you cannot use a simple LERP on a quaternion rotation. There may be a similar interpolation function that works for quaternions: perhaps SLERP (spherical linear interpolation), but I don't recall for sure if that works

#

Anyhow, this technically isn't a networking question, so if you have other questions about moving elements around on a single person's game, you may wanna ask them in #udon-general

frank fjord
#

Hey, what is OnDeserialization's result.isFromStorage boolean? This isn't documented at all as far as I can tell...?

strange gyro
#

Maybe something related to world persistence? I don't remember it being there before

frank fjord
#

oh...!

#

That'd make sense... other than the fact that that doesn't exist yet.

strange gyro
#

They do have a couple of references to persistence in the SDK like the VRCPlayerObject and VRCDisablePersistence components so it's probably just stuff they've been testing internally

frank fjord
#

Okay so I started doing some serialization bandwidth benchmarking and boy I am... thoroughly confused.

I am spamming manual serializations on an object with 1 synced byte variable in it.

I'm running this loop by requesting a new serialization at the end of each OnPostSerialization.

Somehow, serialization events are running 12-13 times a second, which is almost 3 times faster than I thought was normal. Aren't manual serialization events limited to five times a second?

Additionally, this 1-byte object has a serialization size of 14 bytes (12 overhead, 1 byte header, 1 byte data), and I've verified this both with Debug 6 and OnPostSerialization result. If this is running 12-13 times a second - which is weird already - this should be an outgoing ~175 bytes per second. But Debug 6 is telling me that it's estimating anywhere from 500 to 600 bytes per second for that object.

#

What in the heck is going on here...?

#

Additional confusion:

I'm testing a similar object that does the same thing except that it's syncing 4 bytes instead of 1 byte.

The size of the object - and its reported outgoing serialization size - is 20 bytes, which would suggest that each byte is actually 2 bytes in size.

In HappyRobot's presentation they suggest VRChat tries to 'optimize' synced variable headers. They mention this doesn't work very well, as the variables are not grouped together by type, but seemingly some other way. This should not matter if literally the only variables you're syncing are all the same type, though. I would anticipate, therefore, the size of the serialization to be 12 (object) + 1 (byte header) + 4*1 (4 bytes) = 17

#

Gosh I sure wish there were consistent explanations for literally anything going on in the networking layer :S

#

So the only conclusions I can draw from this are:

  1. The size of variables isn't what is documented even in the best-case scenario where you only have 1 type of variable and headers 'can' be shared/compressed

  2. Manual serialization can occur 2-3 times faster than advertised. And then, even factoring in this reported faster rate:

A) 2 to 4 TIMES more bytes are being serialized than is being reported by debug view or serialization result bytes out, and/or

B) serialization events are happening as much as 30+ TIMES faster than reported even by the debug view or by literally keeping track of serialization events in script

#

...

#

Nothing makes sense 🙂

strange gyro
#

Yeah the docs aren't all that helpful with the lack of specific details

#

All they say is that the send rate is influenced by the amount of data you're syncing

strange gyro
frank fjord
#

I only recall ever reading anything like that in the context of 1: continuous serialization or 2: network suffering

#

Oh, so it does.

strange gyro
#

Each manually-synced object is rate limited as a factor of the data size. The more it sends, the more its send rate is limited.

#

Very vague

frank fjord
#

Yeah.

#

Unfortunately, even with that in mind, there is still this weird inconsistency where if you multiply the size of the object you're serializing (or its result.bytesOut, which are the same) by the reported send rate, you get something that is 2-4 times smaller than how many bytes per second the debugger is reporting using

#

14 bytes 12 times a second is like 168 bytes/s

#

which is definitely not even within margin of error to 530 bytes/s

strange gyro
#

Who knows what other kinds of data they could be sending as part of the serialization, could be including variable names or hashes of names to map variable values to, maybe even ownership state data

frank fjord
#

I just ran another test just to make sure I'm not insane. I'm now manually calling RequestSerialization once a second for this 1 byte object. Debug View 6 confirms 1hz send rate. Still 14 byte object. Actual BPS ~44, still off by about 3x

#

Also measuring weird inconsistencies in my outgoing data rate. With no replicated objects, on desktop I'm measuring a steady 1.3 kb/s out. Replicating this 1-byte object spamming, the debug view says it's contributing about 550-560 bytes/s right now, but my overall outgoing data has shot up to 2.15 kb/s.

That's ANOTHER 300 b/s that's just mysteriously there and not accounted for anywhere?

None of this adds up lmfao.

#

This upsets my sensibilities so much, I have no idea how to make good use of literally any of this information or any of these tools if I don't trust them to make any sense.

strange gyro
#

I just pack everything I need into a single synced ulong array and don't pay any attention to the data rates, but my use cases haven't gone beyond 5 ulongs

frank fjord
#

oh, ulongs

#

Still, that'd be what... 40 bytes?

#

Anyone. TL;DR from what I can tell:

  • The docs say you've got 11kb/s. In reality you probably get half that, or less, after factoring out a user's VoIP, IK, and animators. for worst-case scenario.

  • The docs say a byte is 1 byte, short is 2 bytes, etc. In reality, they cost twice as much if you trust object size in the debug view 6, or reading serialization result bytes out.

  • If you actually look at the bytes per second, this 2x cost multiplier goes up to approximately 6.5x per variable. There's no explanation as to where this extra data is coming from.

  • If you look at your total overall outgoing kb/s, this multiplier goes up to nearly 10x per variable. There's no explanation as to where this extra data is coming from.

#

I'd love to be proven wrong on any of this :S

lone zealot
# frank fjord Anyone. TL;DR from what I can tell: * The docs say you've got 11kb/s. In realit...

When serializing meta-data is sometimes added to the actual payload. This is called overhead.
Another factor is padding. Padding is sometimes added to align bytes in memory, which is usually done for performance reasons or because a specific protocol requires it.
Both of these increase the amount of data that needs to be sent in seemingly arbitrary ways. The exact serialization mechanism in VRChat is not public unfortunately.

frank fjord
#

Just really frustrating to be told "here's about how much data you can send" and have that be misleading by a factor of 20x (between the variable padding/meta-data thing, and the 11kb/s being eaten into by avatar and VoIP)

stone badger
frank fjord
#

I just want to feel like I can say "How do I do X?" go read up the information on how to do X on The Source Of Truth That Was Created To Help Make People Do X, and then find success. I'm investing a lot of time into trying to plan out efficient ways to make a project work within the constraints of VRChat's platform but I keep figuring out that my model for what those constraints are, is wrong.

#

Anyhow. It seems like I just have to adjust my expectations as to how much bandwidth I have available at a time down to like... the equivalent of 400 bytes per second.

stone badger
frank fjord
#

I did a couple more follow-up experiments and think I have determined a few more things:

  1. The size of variable headers appears to always be 1 byte. Doesn't matter of it's a byte or a quaternion, just add 1 byte for every variable you have, in addition to those variables' network size (except arrays, which are chonkier). So a byte is 1+1 bytes, an int is 4+1 bytes, a quaternion is 16+1 bytes.

  2. The difference between expected serialization (rate * object network size) and the debugger's bytes/sec goes down the more variables there are on an object. My 1-byte object (14 bytes total) was over three times more expensive to serialize in actual bytes/second (expected 168bps, got ~515bps), but an 8-quaternion object (148 bytes total) was just 1.27 times more expensive in actual bytes/second (expected 1776bps, got ~2100 bps)

  3. The additional mysterious bytes added between the BPS per object view, and the actual seen outgoing KB/s in the debugger, SEEMS to be fairly static. If you're serializing any manually serialized objects, you just add 200-300 bytes/s on top of whatever your total BPS is from objects. I saw no difference between the overhead of my 1-byte object, my 8-quaternion object, and both at the same time, in terms of this extra padding.

#

This... helps. Clearly my estimate for how much bandwidth was getting wasted was overblown

#

But it does mean a few things

#

In particular: lots of objects with very little synced info on them are... bad. Not just because of the 12 bytes of overhead objects carry, but because of how disproportionately they are affected by this extra-extra overhead/padding

hybrid kindle
#

working on a local fog transition in a certain area, issue is when player first joins the density is not at 0.005 but practically 0. The only way for it to fix is if the player goes in the area then back out. Any way to alter this to fix it?

frank fjord
#

Does anyone know what's really happening under the hood when SetOwner() is called on a networked object? The debugger does not show any data sent when it's called, OnPostSerialization isn't called either...

But SOMETHING is being sent over the network, because you can successfully claim ownership of something (and have other players see that with OnOwnershipChanged) without ever requesting a serialization.

Is there an awful edge-case where it's possible for an actual data serialization to arrive BEFORE an ownership change message arrives...?

frank fjord
#

awh no, that is not something I wanted to discover

#

So. When you receive a serialization event from another client...

#

OnDeserialization() runs in order of network ID NOPE

#

but, when OnDeserialization() runs for a given object, only the updated data for that object and objects that have already run their own OnDeserialization() event is available.

#

All objects with higher network IDs, that haven't run OnDeserialization() yet, will show their old, unupdated data.

frank fjord
#

oh no.

#

How DO you change something's network ID manually?

random parrot
frank fjord
random parrot
#

hope you're doing well

obtuse echo
# frank fjord Does anyone know what's really happening under the hood when SetOwner() is calle...

There shouldn't be any edge cases if you change owner and serialize data in the same frame. That's the recommended way and it has worked for me consistently. The data is always bundled with the owner change properly.

And yeah, I don't think you can count on multiple OnDeserializations to trigger together in a predictable order, at least in my experience. You have to use something like a timestamp to coordinate them and detect when all required data has arrived.

onyx path
#

so for this climbing asset I am supposed to enable swimming and climbing in my collission matrix, however mine does not include such options?

timber ferry
frank fjord
#

Okay I learned two new things that both suck lmao.

OnDeserialization() does run for late joiners, but it will not accurately reflect the current owner for the object it's run on.

OnDeserialization() seems to MOSTLY run in order of network ID... except when it doesn't. I tested this extensively by myself but then as soon as I tested with some other players and someone left and rejoined, I started getting inconsistent results. Network ID does NOT deterministically, 100% of the time guarantee order of OnDeserialization(). And this sucks because that means you cannot guarantee an order that data arrives in.

#

So. I have a non-serialized object which references two different serialized objects: A, and B. A is always necessary, B is sometimes not sent for bandwidth optimization.

Originally, I thought network ID determined order of deserialization. So I had object A's network ID higher than object B's, so that object A could tell its parent object to do things when OnDeserialization occurs, with the confidence that - if object B WAS serialized - its data would already be available to use.

Now I realize this doesn't work. Despite object A definitely still having a higher network ID than object B, sometimes object B runs its OnDeserialization event later, and thus its data is sometimes not available by the time object A runs its own OnDeserialization event.

#

What the heck are you supposed to do about this lmao.

#

If synced variables on a given object are only guaranteed to be updated by the time OnDeserialization runs, but you also can't guarantee an order that different objects will run OnDeserialization...

#

That makes it very hard to break out important vs less important synced variables into different objects to save bandwidth

frank fjord
#

I seem to recall there being SOME way to influence script 'execution order.' Would that have any effect on the order in which deserialization events are run...?

wispy arrow
# frank fjord I seem to recall there being SOME way to influence script 'execution order.' Wou...

That would be DefaultExecutionOrder(int order), lower means it executes sooner. More information on that is available here: https://docs.unity3d.com/2022.3/Documentation/ScriptReference/DefaultExecutionOrder.html

An example of this in action would be ProTV 3, which has a default execution order of -9999 to initialize first as seen here: https://gitlab.com/techanon/protv/-/blob/release/3.0/Runtime/Core/TVManager.cs#L18

frank fjord
#

I guess I'll just have to test it to see if it affects OnDeserialization order...

wispy arrow
#

You never know unless you try!

#

¯_(ツ)_/¯

frank fjord
#

aye

#

Thanks for linking the Unity docs though. I didn't think to read those in addition to the VRChat docs, and had sorta given up on that attack vector hours ago

#

I think, awkwardly, I'm learning to trust the docs less and less.

wispy arrow
#

I’m gonna be real, I don’t really read the VRC docs

#

There’s so much stuff that exists in Udon that just, isn’t documented whatsoever

frank fjord
#

... yeah. ;u;

frank fjord
#

Bummer, back to square one. Really hoped that'd just work lol

#

Time to find out how good or bad an idea it is to just kick the Deserialization logic down the road one frame with a Custom Event Delayed Frames... all to make absolute certain I've got all the deserialization updates I'm waiting for.

#

Surely nothing can go wrong here.

lone zealot
#

@frank fjord DefaultExecutionOrder is indeed only for the "internal update loop" inside of Unity https://docs.unity3d.com/Manual/ExecutionOrder.html
VRChats Networking Architecture is a little bit difficult to work with I agree. The basic idea however is relatively simple.
All synced variables in your world together form whats called your "state" and everything you derive from that state is called "view"
The view should always reflect the current state and should be fully derivable from just the current state alone.
As long as that can be guaranteed and you build your logic around that concept its fairly straight forward.

frank fjord
lone zealot
#

Yeah its not really possible currently to really optimize network bandwidth due to how VRChats networking works :/

frank fjord
#

well, you have to optimize it SOME how

#

Like, if you want there to be room to send all the data you NEED to send, you have to find a way to reduce or pack or craftily break up what data you're sending, and when

#

otherwise you just don't get to make the thing you're making x'D

lone zealot
#

Well the best you can do is seperate synced variables that dont get updated/read together into seperate behaviours.

#

And be really clever about removing redundancy in your state

frank fjord
#

I'm making a series of grabbable controls. If a player is operating any one of them, no other player should be able to operate any one of them. This necessitated some sort of shared state between the controls.

But the controls also had all this extra data that was specific to the particular control.

I really did not want to have to send all the data about all the controls every time anything happened to any one control

#

As a result, I wound up with a synced "flags" object that updates whenever any control is grabbed or dropped, and several "data" objects - one per control - that only need to sync when that control in particular is interacted with

#

But this does mean that a given control needs data from two different synced objects in generally the same period of time... and that's how I ran into this issue of not knowing which OnDeserialization event was safe to trigger logic.

lone zealot
#

Yeah unfortunately VRChat does not allow delta-serialization :/

frank fjord
#

Yeah. This is frustrating :S

#

especially with bandwidth being so limited as is

strange gyro
#

At least the exclusive ownership part is taken care of for you, just have a manager object that the controlling player takes ownership over and reject player interactions if they don't own the manager object

#

You're in control of when data is synced so the flag per control is probably not needed

#

Since the player has exclusive ownership over all controls

#

OnDeserialization only gets called for the controls you requested serialization for

#

Except for a first time join

random parrot
frank fjord
#

It's trivial to go "hey is someone else holding and owning this right now? If so, don't grab." But what if both parties don't know that the control is grabbed yet? This race condition was causing my controls to bug out and lock up in all sorts of ways. All of the shit I've been talking about, testing, iterating and fixing has been specifically to make sure this race condition resolves gracefully.

frank fjord
random parrot
#

i think in unity it would be(not sure about that)
but it isn't in udon

frank fjord
random parrot
#

I know from experience that it doesn't :(
a painful time that was

frank fjord
#

I believe you >.<

bitter sentinel
#

Can somebody explain how the networking ID system works to me?

frank fjord
# bitter sentinel Can somebody explain how the networking ID system works to me?

Generally speaking, all gameobjects that have a networking script attached to them (so a script that is doing manual or continuous synchronization) are assigned a networking ID on build. These IDs are used for clients to identify what 'thing' in the world is being talked about when network information arrives.

If I tell you "that ball changed position" you have zero clue what I mean by "that ball" unless we both have some way to agree what "that ball" is. That is what the network ID is for.

bitter sentinel
#

Ah

#

That makes sense

frank fjord
#

This means that if you somehow get yourself into a state where different clients have different network IDs for their object, your networking won't work. The three big examples of this are:

  1. Two different versions of the same world (for example, you just pushed an update)

  2. Two different platforms of the same world (PC vs Quest); you'd need to carefully vet your networked object IDs, or use the network ID utility to export and match up those IDs, otherwise if you have significant differences between the two platforms of your world you may wind up with different IDs for your objects

  3. Instantiating new instances of a networked object. Don't do this. When two clients instantiate new instances of an object, they have no way to agree on a network ID for that new object, and thus any networking code on those objects will fail. Instead, make a pool of inactive objects before you build and recycle from that pool. This way, all those objects will have network IDs generated on build, and all players will agree.

bitter sentinel
#

Alright, thanks! That helps a lot

frank fjord
#

if OnPostSerialization result.success returns false, will another serialization attempt automatically occur next time it can? Or are you SOL and have to request another serialization manually?

cold laurel
frank fjord
#

Well. I guess I expected this but, still a bummer. First draft on my calculus-wielding flight predictive sync system is... pretty lame.

#

It's just not that smooth, all things considered.

#

Now I have to somehow figure out how to profile and log this sufficiently to figure out what its weak point(s) actually is

frank fjord
#

So. My implementation of manual serialization, extrapolative prediction of the airship's flight path, and smoothing between last simulation and new prediction...

... is not great. Failing grade. It """works""" but it's quite jittery. The smoothing between corrections does not sell it as a nice, continuous motion at all.

Embarrassingly, simply replacing my manual serialization logic with a continuous smoothed serialization of just the position and rotation variables (see: NOT doing VRCObjectSync) and then setting position and rotation from those values every frame... was a lot smoother. Not perfect, it definitely comes across like a baby's-first-synchronized-object sorta deal and I'm not proud of that. But it beats out my more complicated code by a lot... and the serialization bandwidth of both implementations is about the same: 300 BPS.

#

VRCObjectSync was an abhorrent failure. Worst implementation of the three, 0/10 would not recommend.

#

...

What a bummer. While it's cool, I guess, that there is a significantly "simpler" implementation that does what I'm doing, but better... it's also not really "great." And I can't improve "just continuously sync the variables" because all that logic is happening under the hood.

#

Also, because it's not VRCObjectSync (or my original implementation), I don't get any local physics simulation at all. I effectively am treating this physics object like a kinematic object and moving it frame by frame.

cold laurel
#

Sacchan's aircraft sync uses a PID-like system for smoothing with some extrapolation and ping correction.

#

But no client-side physics

#

since it would be too expensive with many vehicles

cold laurel
cold laurel
frank fjord
# cold laurel Since I'm assuming you only have one airship in this case, doing client-side phy...

So... there is one PLAYER aircraft. All players share that one aircraft. There will also be some number of AI-controlled aircraft that a player will have to simulate and replicate to other players, but 1) I don't expect the number of LIVE ai-controlled ships to be very high (definitely no more than 10, probably even fewer) at a given time, and 2) the accuracy and smoothing on those can be a little more lax than the player-operated ship, since there are no players onboard to be given motion sickness :S

frank fjord
frank fjord
#

So T is not changing linearly, but increasing from 0 to 1 with easing on both sides.

frank fjord
#

Not heard of this 🤔

frank fjord
#

Oh boy. Reading time...

#

I'll chew on this some.

cold laurel
#

There are many good YouTube videos on this topic

restive cliff
#

Don't worry, there's example of Unity C# code for PIDs scattered on the internet too.
The tl;dr is that you can feed it a target value and the current value. The PID process will try to get the current value to reach the target value by tuning the P, I, and D parameters

cold laurel
restive cliff
#

holy smokes I should have encoded the video I'm uploading

#

While my video does talk about smoothing instead of PID, do you have suggestions on that @cold laurel ?

#

Other than the PID, you could sacrifice a bit of latency to smooth the interpolated result like this video below. I do have to speed up/slow down the smoothing based on the deviation from the interpolated result since there is non-deterministic drift from having the rigidbody not being kinematic, and has the effect of dead reckoning when there is no expected result when the interpolation reaches 1.
The top one is the owner, the bottom is the observer. That jittering blue mesh is OnDeserialization result.

#

(fixed) oh neat I forgot to edit out the second half of the video

frank fjord
frank fjord
# restive cliff

This looks really good. Whatever interpolation is being used - at least at this speed and framerate - doesn't produce any noticeable jerks or rubber-banding. Impressive.

restive cliff
frank fjord
#

Is this unideal?

restive cliff
#

I feel like it would be okay for aircraft with local collision added

#

in some games, they just make the seemingly disconnected player disappear if too much time has passed since the last snapshot

frank fjord
#

oh I see.

frank fjord
# restive cliff

To clarify then, this is your work? And this implements a wholly smoothing-based approach, without a PID controller?

restive cliff
#

yes

frank fjord
#

Very cool 🙂

#

How precisely are you implementing the smoothing?

restive cliff
#

imprecise actually. I was hoping for Udon 2 to go towards a more deterministic local simulation. I let this one drift away 1 meter from the expected position before speeding up the smoothing. Or just snapping it if the car teleports a minimum distance

frank fjord
#

I'd be surprised if Udon2 made anything more 'deterministic,' the physics system is just PhysX for Unity and PhysX has - to my knowledge - always been non-deterministic

#

I don't think Udon or Udon2 would even be able to touch any of that. Perhaps I'm misunderstanding what you mean by "a more deterministic local simulation?"

restive cliff
#

I am at the mercy of physx for the rigidbody, but everything else is in Udon at the moment. E.g. the tire forces and downforces. So I would resimulate the vehicle for non-owners in Udon 2

frank fjord
#

Incidentally, I just meant to ask "how are you implementing the smoothing," not "how precise is your smoothing" ;P

restive cliff
#

Even running 1 vehicle's physics for the owner is at the great cost of CPU since I wanted ISO 8855:2011 for the vehicle physics. The clients just get the position, rotation, and velocities

#

I am currently using a hermite spline with acceleration for the interpolation. And the extra correcting and smoothing is currently some arbitrary in-house stuff that looks at the deviation of the vehicle compared to the expected interpolated path

frank fjord
#

Full hermine spline? Or a simplification of it?

#

I'm also not sure what you mean by a spline "with" acceleration. I also presume you are doing interpolation for all three of the replicated values?

#

And you said you had to change the length of the interpolation based on the magnitude of the initial error, yeah?

restive cliff
#

I'm not sure if it's full or a simplification. I do plug in the linear and rotational velocity

#

I have last looked at that part of the code in october last year

frank fjord
#

Gotcha. I think I recall someone implementing a 'simplification' of the hermite spline that effectively looked like a lerp between two lerps, where each of the two lerps represented some 'control point' calculated in a way I don't recall. The full hermite spline involves a lot of straight algebra and I don't think is TRULY replicable 1:1 by a couple lerp functions...

restive cliff
#

My lunch break has ended but I will be peeking in here

frank fjord
#

but I was curious how the two compared in practice.

#

hence my asking

restive cliff
#

oh.. I am not using lerp functions

frank fjord
#

Thanks for the thoughts. I'm gonna dig into PID controllers a bit, since my last attempt to use dampening/smoothing clearly did not end well. And, lacking a good example of an alternative smoothing system I can deconstruct, I should probably try something else to begin with x'D

restive cliff
#

one helpful resource for PID tuning that I recently used was this one https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=9013

Thorlabs specializes in the building blocks for laser and fiber optic systems. From optomechanical components to telecom test instrumentation, Thorlabs' extensive manufacturing capabilities allow us to ship high quality, well priced components and devices for next-day delivery. Optomechanics, optics, opto-electronics, laser diodes, fiber optics...

restive cliff
#

I can confirm that KitKat HAS indeed passed out before I suggested smoothing. So they could be responding later too

frank fjord
#

Hm. I guess one thing I need to determine is... what actually in the system is being taken over by the PID controller?

My airship has three piloting controls: the wheel for yaw, the torch for altitude, and the throttle for speed. Each control describes a 'force target,' which the engines then spool up or down to at a fixed rate (so if I throttle to max, the engine may take up to 2 seconds to go from 0 force to 100% force).

These forces then act on the airship rigidbody, producing an angular velocity for the yaw, a "forward" velocity (in the airship's forward direction, on the Z/X plane), and a vertical velocity that is obviously independent of the yaw.

These velocities then produce a rotation and position in space.

If I want the airship to 'simulate' locally on remote clients when there is a gap in network activity, my initial intuition says I need all of this data to continue the simulation. But then where exactly does a PID controller even come in? Does the effect of the PID controller replace some of these variables? Act on top of some? Not others?

#

I suspect... what's actually going on is I have to store two rigidbodies still

#

one of them being my local simulation, the other being the current simulation from network data.

#

When I get a network update, the invisible network rigidbody inherits the network data - maybe plus a forward prediction, I'm not sure yet, that has seemed pretty unstable so far - and then continues simulating in real time until another network update occurs.

Then... perhaps I'm applying forces to the local simulation rigidbody in proportion to the positional and rotational error, between it and the network simulation above?

#

The target is always moving, technically; network updates arrive every so often but during the in-between frames, that data needs to be doing something, going somewhere

restive cliff
frank fjord
#

God on the one hand it's so cool I can just read this and pick it apart—

On the other hand it is SO hard to read and pick this apart, with none of the context or headspace it was written in

frank fjord
#

Guh. The one thing that keeps tripping me up with forward prediction is that I need an estimation of acceleration and acceleration rate of change to do the calculus

#

the estimates are just... really bad without that

#

Unfortunately, unless I'm missing something clever, I only have two options to do that:

  1. keep a 2-frame buffer, EVERY frame, where I calculate acceleration since last frame (and stash the acceleration before that too). Then, serialize 2 extra floats and 2 extra Vector3s every time I serialize...

... or 2) Receive the serialization without that data on the remote client, do nothing for more frames than I'd like (it's more than 2 because rigidbodies take a sec to 'catch up' with changes you make to them/their gameobject transforms) while I locally simulate and catch my own acceleration estimates, then use those

#

I'd LOVE to just do more calculus, calculate it from my forces I've already received in the serialization

#

but

#

Drag :S

#

I do not know how to integrate drag.

frank fjord
#

Well. My first few hours working with PID controllers have not been particularly promising.

#

They seem to work... mostly. But they have a really bad tendency to enter death spirals... quite literally. Certain tunings almost immediately result in an ever-increasing orbital oscillation around the target that cannot be recovered from, and even when I adjust the tunings to something that LOOKS promising, flying my target around for less than a minute usually results in spontaneously entering a death spiral at some point anyways.

#

I also almost feel like I need smoothing or lurch-discarding on top of this, too. It's very easy to get the occasional misfired prediction that momentarily launches the follower a few feet because the PID tuning is actually really GOOD; it too quickly accelerates into bad data, and I wind up with rubber-banding anyways.

frank fjord
#

Maybe I should stop working on smoothing for a bit and spend some time trying to figure out how to sanitize the data I'm getting in the first place. The smoothing is definitely busted on its own but not getting particularly clean updates is not helping this matter at all... :S

#

... well. After sleep, that is. I'm so tired.

cold laurel
cold laurel
# frank fjord Gotcha. I think I recall someone implementing a 'simplification' of the hermite ...

Cubic Bezier and cubic Hermite are pretty much the same thing but with a different representation. You can calculate a Bezier with lerps, and can thus also calculate a Hermite spline the same way. To convert Hermite tangents to Bezier control points you just need to divide the magnitude of the Hermite tangents by 3. To get the Bezier control points for the other side just flip the Hermite tangents.

cold laurel
cold laurel
random parrot
#

Freya Holmér makes some amazing stuff

sage trail
#

this should be a simple code for a collider when a player enter the room the number of players should increase and if someome leave the number should decrease

#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UnityEngine.UI;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class OnCollisionUpdate : UdonSharpBehaviour
{

    [UdonSynced] int playerNum = 0;
    public Text Number;

    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
    {
        Debug.Log("Entering Happened");
        Networking.SetOwner(Networking.LocalPlayer, gameObject);
        playerNum++;

        Debug.Log(playerNum);
        Number.text = playerNum.ToString();
        RequestSerialization();
    }

    public override void OnPlayerTriggerExit(VRCPlayerApi player)
    {
        
        Debug.Log("Exteting Happneed");
        Networking.SetOwner(Networking.LocalPlayer, gameObject);
        if(playerNum <= 0)
        {
            playerNum = 0;
        }
        else
        {
            playerNum--;
        }
        
        Debug.Log(playerNum);
        Number.text = playerNum.ToString();
        RequestSerialization();
    }

    public override void OnDeserialization()
    {
        Debug.Log("The Deserialization");
        Number.text = playerNum.ToString();
    }
}
#

but what happened that I found when I try to request serialization it's not running for the local player so I made it happen localy

#

but then I found a negative number so I added the IF statment.

#

but now there's something I don'ts understand about the master of the room or the network owner that making if there's tow people some times it saying 3 vrcBotThink

jaunty fjord
#

when you RequestSerialization for the local player, you also need to include the OnDeserialization on the next line

#

the local player doesn't call OnDeserialization, you have to call the function manually

frank fjord
frank fjord
#

Looking a little at Sacc's code it appears they are doing a simple linear "forward" extrapolation but I can't for the life of me suss out how they're determining the magnitude of that extrapolation.

#

Dense code is hard to read sometimes.

cold laurel
#

This is the sensible thing to do.

frank fjord
#

Even in a small window of time (say 200-300ms), acceleration - and even acceleration rate-of-change - can have a surprisingly large effect on predicting final position. As can rotational velocity/acceleration/accel-rate-of-change, if your linear acceleration is always directed in your forward-direction...

#

I would have liked to assume it wouldn't but when I mathed out the frame-by-frame physics it made a difference on the order of tens of centimeters.

cold laurel
#

How fast are these air ships??

frank fjord
#

Right now I'm testing with a nominal top speed of ~20 meters per second

#

But honestly the speed doesn't (shouldn't??) matter because the error is always going to be a ratio to the speed, and that ratio determines the "noticeability" of error to the player's eye.

cold laurel
#

Both Sacc's airplanes and Techa's cars are way faster than this and are fine with not accounting for acceleration.

frank fjord
#

Right

#

So. What else is happening? This is what I mean by systems tolerating larger errors/less precise predictions

cold laurel
#

Latency

frank fjord
#

... also I don't think Techa's cars are doing forward prediction at all

#

right?

#

Maybe they are.

cold laurel
#

Afaik @restive cliff isn't currently doing any extrapolation for the cars.