#udon-networking

1 messages · Page 27 of 1

twin portal
#

for simplicity for now, it could just be a button that sets the role for the local player

barren scaffold
#

^ Udon deleted that script yesterday for some reason.

#

I can set it back up its not hard

#

Just instead of it handling the roles itself have it send a customevent to the rolemanger?

twin portal
#

that's right

#

so if I just assume that the script is a button that sets the role for the local player...

#

the beginning and end should be something like this

#

we know we start with Interact, which eventually leads to calling SendCustomEvent on an UdonBehaviour.

#

so how would these connect together to do what we want?

barren scaffold
twin portal
#

you're on the right track

#

I think just finding the specific gameobject is the only task you haven't figured out yet, with that you'd basically be done

barren scaffold
#

Objectpool?

twin portal
#

naur

barren scaffold
#

I'm scouring the wiki looking...

twin portal
#

that might not help. This is more of a logic puzzle.

#

so our GetPlayerObjects array, it's a list of potentially multiple GameObjects. but one of them is the object we wanna mess with. The array could look like this:
[Cube_01, Cube_02, RoleManager, Cube_03, GunThing]

Or RoleManager could be at the start of the array. or the end. But we know it's in there somewhere.

barren scaffold
#

Right

twin portal
#

our For loop will, essentially, "look" at one GameObject at a time.
...so we'd need some sort of test, no? A test that only the GameObject RoleManager will pass.

barren scaffold
#

Udon blanked my code again.

#

Why does it keep doing this?

twin portal
#

no idea

#

graph moment

barren scaffold
#

Fucking christ.

twin portal
#

sometimes happens when the graph fails to compile

#

it can get confused when that happens sometimes

twin portal
#

what int could you get off of a GameObject?

barren scaffold
#

Location?

twin portal
#

hm... not sure if that would work.

#

what would be unique about RoleManager's location, compared to other PlayerObjects?

barren scaffold
#

So the name? String?

twin portal
#

bingo

barren scaffold
#

Because thats the only thing that changes, how do you reference that everytime it changes?

twin portal
#

loop through the PlayerObject array, use the String.Contains node. Find the GameObject that contains the name "RoleManager". Then we'll know that's the right object

#

note to use Contains, not check for the name to be exactly "RoleManager".
When the PlayerObjects are instantiated, their name will add a number to the end of it, so each player's object will have a unique name

barren scaffold
#

So contains checks just for the name? If it matches the name (Even with extra characters) it'll let it through?

twin portal
#

pretty much does what it says on the tin yeah

barren scaffold
#

Cool

twin portal
#

you give it a string, if that string contains the other string at any point within it, the bool will output true

#

so you need to plug the GameObject's name into instance, and "Rolemanager" into the value

#

(either that or the other way around. some nodes are a bit hard to parse compared to their original C# functions)

barren scaffold
#

Its not liking the gameobject being an instance

#

Wait, do I need to convert the game object to a string also?

twin portal
#

you need to get the "name" property from the GameObject

barren scaffold
twin portal
#

that's it

barren scaffold
#

Next would be branch, if true send custom event and if false let it end, right?

twin portal
#

should work yeah

#

just one piece missing, the GetComponent

barren scaffold
#

Gameobject?

twin portal
#

right. What GameObject goes there?

barren scaffold
#

The one were looking for. So we would get it from gameobjectarray.get

twin portal
#

that's right

barren scaffold
twin portal
#

this should do something

#

well, once you actually have a Rolemanager PlayerObject.

#

but that should be easy to make for a test

barren scaffold
#

Right, I put a log at the end to test it, so in this case we don't need a public variable for the object were checking for, right?

twin portal
#

nope, Rolemanager should manage its own variables

#

it just needs that CustomEvent

barren scaffold
#

So problem, since the contains was a char it crashed, if I set it o string I lose the flow.

twin portal
#

change the dropdown in Contains to (string)

barren scaffold
twin portal
#

that's fine. I'm actually not sure why the Char version even has a flow input

barren scaffold
#

So its saying that the bool is sending a fail

twin portal
#

in the Branch there? yeah, it's gonna fail for any PlayerObjects where the name doesn't match

barren scaffold
#

Shouldn't it be matching here? Rolemanager is already made...

#

Its under the playerobjects, its duplicating when tested

twin portal
#

should do

barren scaffold
#

🤷‍♂️

So we just need to move on than?

twin portal
#

does Rolemanager have its script on it yet?

#

this whole graph assumes its there

barren scaffold
#

Gameobject, Udonscript, and udon behavior is all present

#

Its not feeding anything

twin portal
#

those events need to actually do something in order to compile

#

can have them just Debug Log

barren scaffold
#

Mmmmm still getting false...

twin portal
#

can you have the loop print out the name of the GameObject, to see which ones it's checking?

#

RoleManager is not a PlayerObject

#

PlayerObjects is your PlayerObject.

barren scaffold
#

I thought children were affected by parent playerobjects?

twin portal
#

The GameObject with the PlayerObject component, and all of it's children, will be one PlayerObject

barren scaffold
#

Oh so I need to seperate them out

twin portal
#

yes either give Rolemanager and Nametag Script its own PlayerObject component (and remove the one from PlayerObjects), or you'd have to rework the graph to search for "PlayerObjects" instead, then search through that object's children to then find the name "Rolemanager"

#

one of these sounds a bit easier than the other.

barren scaffold
#

Hahaha most definitely

#

Alright its working

twin portal
#

great. I think that's the hard part done. Now as long as you've got a VRCPlayerApi, you have a way to assign a role to them

barren scaffold
#

🙂‍↕️

Now the rolemanager, unless your down to help me tomorrow with it

twin portal
#

I lurk here and help whenever

barren scaffold
#

Fair enough lol, I need some sleep after all this 🥱

misty mountain
#

I have a question about the 10Hz cap in VRC.

I was reading an article about eye and face tracking, particularly face tracking while talking. It’s recommended to keep visemes enabled because the face tracking is limited to 1-10Hz updates. In contrast, other VR platforms offer 20Hz and even higher update rates.

I’m curious to know the reason behind this low synchronization cap. Was it intended to prevent past abuses? Or is it aimed at ensuring that mobile standalone headsets can handle the workload? Perhaps it’s also meant to prevent overloading the P2P broker (man-in-the-middle VRC)?

I’m interested in understanding the purpose of this cap on synchronization rates if other systems are capable of higher ones.

I’m asking because eye tracking and face tracking are part of my 3-6 month upgrade plan, and I want to gain a better understanding of this aspect of VRC if possible.

minor gale
misty mountain
#

By bandwidth cost do you just mean the financial cost of paying for the bandwidth?

(I feel like I could interpret bandwidth cost also as network / computational resource strain)

minor gale
#

vrc runs instances on photon, so yes they likely pay photon on some usage basis; i don't believe it's a cpu consideration

north thistle
#

VRC has hard limits on how much data a client can upload to the VRC servers per second, likely to keep the server costs down. And to a seemingly large degree (it's never fully explained, mostly just vaguely alluded to), that bandwidth limit is shared with world network usage. So the more networking avatars use the less networking a world has to use for its stuff.

finite sierra
warm steeple
#

can there be a case where an event gets sent from the owner but not received on others? or vice versa, where for some reason the owner doesn't receive an event or doesn't even send an event when it should

#

not taking rate limiting into account

#

I'm thinking about network connectivity issues

north thistle
#

I'm pretty sure the only time that should happen is if the sending player times out

warm steeple
#

I guess this is why there is a reset button in some worlds

#

that's the simplest way to recover

#

though it's also used as a way to cover one's ass too

heavy spindle
#

Usually if worlds get in funny states as a result of the owner disconnecting and ownership is passed on to someone else, things can be recovered. Though it's usually better to just harden your logic to such things. Not always possible though

timber parcel
#

If two players ask for ownership of the object at the same time. At what point can one be sure that they own it? At what point can one be sure they don't own it?

#

I guess one is sure that they don't own it if they recive OnOwnershipTransferred(anyone else)

glass lintel
#

welcome to the two generals problem

#

hint: there is no solution

timber parcel
#

I feared that might be the case

minor gale
#

the real solution would probably be more along the lines of arbitrating it with a player

#

from what i remember testing you can actually get serverside confirmation of ownership via serialization, in that if you override ownership request, request ownership and request serialization, the serialization won't actually go out until you become the owner in the server's eyes

it's very poor practice however as a failed ownership request leaves the serialization request flagged, i'd argue that the current situation for the ownership request override makes it very undesirable to use because of this side effect

timber parcel
minor gale
#

there's a pre and post serialize event, it behaves differently when you override the on ownership requested function, but again i wouldn't recommend trying to use it for confirmation of ownership

timber parcel
#

Is the post serialize event triggered by the server responding to the serialize attempt?

minor gale
#

it is not, it comes after you serialize the data (locally)

timber parcel
#

Oof.
So if two clients both
Set Owner (self)
Synced variable += 5
Request Serialization

They'll both set themselves as owner, and then send the data without actually knowing if they are the owner.

And the only sign that it failed is when one of the clients gets a OnOwnershipTransfered(the other client).

minor gale
#

yes, a race condition, in a case where you're just incrementing something it'd be better to inform the owner to increment

hoary frost
#

Now I wonder, if youre the owner, would sending a networked event to owner only not use any network bandwidth? Since its effectively to self

minor gale
#

well i say better, but i'd consider it safer personally; if you don't care about losing some data ownership transfer is fine

minor gale
#

not actually sure

hoary frost
#

Would be cool if it did on its own

#

Dunno if it does under the hood

minor gale
timber parcel
# minor gale well i say better, but i'd consider it safer personally; if you don't care about...

Data loss is actually the reason for the inquiry in the first place. If only one player performs those actions then the synced variable is more reliable and less network taxing than a networked event. Especially if that one player is performing a math's operation multiple times in a row.

I'm exploring the case where multiple players request to determine how much of a issue the race condition is and how much I need to engineer for it.

If needed I can have one intermediary per player which will have the most reliability, but that could have exponential scaling problems instead.

So I'm probably going to have some sort of middle ground.

hoary frost
minor gale
#

just to be clear, you'd still use a synced variable in what i'm describing, but the interaction would not be at the whims of ownership transfer

#

with events there is the case of the owner/processing player being unresponsive, so i wouldn't say it's the perfect solution, though i consider it preferable* to the alternative

timber parcel
#

Again network events are the problem in the first place.

I guess I should have said more reliable OR less network taxing. As depending on the implementation it's one of the other, in VRC's case it seems to be taxing.
#udon-networking message

minor gale
#

at what rate do you plan to operate on the value? i can't imagine a player driven scenario where it would clog the network

#

i.e. player interacts with some element

timber parcel
#

This endeavor is all about scalability, edge case stability and reliability.
Network events (and RPCs in general) are a magic bullet, they work well and do a lot for you, but they are also expensive.

minor gale
#

they have some amount of overhead, that's certainly true, but i think you're severely overestimating the weight; if the case is sending an update to a value every frame i can't imagine advising someone to do that in manual sync either

timber parcel
#

That's a good point, I'll switch the intermediaries to continuous sync. I'm willing to sacrifice promptness.

minor gale
#

the point i'd hope to make is that it's likely not necessary to send your update every frame, continuous will handle the rate for you, but you could handle it quite well in your own implementation on events or manual for that matter and control the rate yourself; if continuous works well for your case that's good though

timber parcel
#

To be clear I'm trying to build a data transfer framework that I can scale into a number of different games

safe path
#

How demanding would it be to have six players each sending a network event that updates an integer every second?

olive vessel
safe path
olive vessel
safe path
#

I’ll do that later if it’s absolutely necessary

north thistle
glass lintel
#

And that is why my game start waits for all players to respond, if not everyone responds, it fails

north thistle
#

My game, virtually every single networked object runs on a state machine, so that helps a lot

finite sierra
# olive vessel thats 0.036% of your bandwith

it's sadly way more then that. a byte cost 2 bytes to network, a int cost 8 bytes in udon. and so on. for instance a long cost 16 bytes. and since its also in Manual. each second that means each person is gonna output 20 bytes per second roughly. considering that just having manual mode on a script cost 12 bytes. and then there is cost of the handshake that is done in manual mode. which makes it cost even more.

#

not to mention you don't really have 11 kb/s or even close to that. there are also other factors such as the amount of players in an instance, the things on your own avatar, fbt, face tracking and eye tracking also counts towards that max.

#

even for my own creation which only has 24 bytes synced once per some amount of time. in manual mode. and then a more regular 20 byte event. combined around 44 bytes at worst case every 30 second. which in the case of my world with a limit of 48 players. was previously 80. which was not stable. which then was reduced to 60. which was stable to some degree but to many avatars so it was eventuelly reduced to 48. which has been proven to be very stable. but with a low chance of it running into some udon limit for 1 or 2 people.

#

and at 48 people even if you did not sync anything it would hover around 9 kb/s give or take. which makes no sense if it is only 44 bytes at worst per 30 second.

#

since 44 x 48 = 2112 bytes. and having 48 people in an instance seems to use a fairly large chunk of the available data. have still to confirm exactly how much it is per person. but i have somewhat estimated that each person joining cost roughly 145 bytes per so 48x145 is 7000 bytes

olive vessel
#

intresting, do u know the size of the evnt overhead. i might need to batch some of my event then

finite sierra
#

Events seems to have a overhead of 12 bytes or so. and so does it seem to be for just adding [UdonSynced] to any script all through thats a single time cost.

timber parcel
north thistle
timber parcel
north thistle
timber parcel
north thistle
#

Very fair

safe path
#

Thank you very much for the explanation

minor gale
# timber parcel I was gonna hit you with the "Well you know what they say about assumptions" Bu...

i apologize if i came across as pushing events too hard or if this is unsolicited. from my perspective it did sound like you want something resembling events without calling them events, but i definitely don't know the full context/intent

i put together a small tangible example of a simple value modification. it's how i might approach a state-based system where each player is responsible for their own intermediary as you called them, in a scenario where they want to advertise a change to some central value

continuous could be applied to this, removing the need for the timing and serialize request, but continuous will have inherent delay and broadcasts continuously, regardless of changes, so i'd prefer manual personally

there are real bandwidth benefits to using state over events, for bundling changes the benefit can be pretty large; though the boilerplate/routing of state is larger than events. i use both where applicable

[UdonSynced] private int requestedValueModification;
private bool runningSend;
private const float dataPushMinimumInterval = 0.25f;
private double lastPushTime;
public void _ModifySendValue(int delta)
{
    // I initially forgot, but you'd want to check for the local player being the master/owner of the system at this point and branch if they do not need to advertise over net
    

    requestedValueModification += delta;

    if (runningSend) return;
    runningSend = true;

    double timeSinceLastPush = Time.timeAsDouble - lastPushTime;
    lastPushTime = Time.timeAsDouble;

    float delay = (timeSinceLastPush >= dataPushMinimumInterval)
        ? 0f
        : (float)(dataPushMinimumInterval - timeSinceLastPush);

    SendCustomEventDelayedSeconds(nameof(_PushData), delay);
}
public void _PushData()
{
    RequestSerialization();
    runningSend = false;
}
public override void OnPostSerialization(SerializationResult result)
{
    requestedValueModification = 0;
}

public override void OnDeserialization(DeserializationResult result)
{
    if (!Networking.IsMaster) return; // Assuming the master is the owner of some central system, an owner check is more applicable with a specific reference

    // Handle the value at this point, likely passing the value to a central handler
}
north thistle
#

Also in terms of networking performance, events might be slightly more performant than manual sync. At the very least, vrc lets you send out more events per second than manual syncs (maybe due to manual data needing to be saved on the vrc server while events can relatively pass through).

Continuous is more performant per sync, but the fact that it constantly, rapidly sends out syncs with no good way to control it makes its in-practice network cost prohibitive.

A good middle ground is VRCObjectSync but that only works for syncing transform and ribibody info

timber parcel
hoary frost
#

Note that delayedframes is also present

minor gale
#

it's not free to run delayed events, but i haven't tested the specific case of time checks as noted in the thing i'm forwarding here (so i do not know); at the level of a single object i would take the thing that is easier to invoke or most readable

hoary frost
#

Id assume delaying by 100 seconds would be cheaper, rather than incrementing each frame for 100 seconds, testing each frame if its been met

#

Maybe theres a sweet spot where the overhead for queuing an event is cheaper

#

Thats definitely overthinking it huh XD but thats fun

minor gale
#

in that specific case i was emulating an update loop by calling a delayed frame event for the next frame every frame; instead of using update, i didn't expect it to be especially slow, but it was substantially slower in that test environment

hoary frost
#

Also Update() on its own has overhead even when empty

#

But senddelayed doesnt

#

Like, i mean, when not called or interacted with

#

To disable update() youd need to turn off the gameobject

minor gale
#

yes if it's something you do sparsely it could be preferable to use over update

hoary frost
#

Yeah

#

As an avid user of many, many scripts that happen once in a blue moon i imagine overall performance would be much worse if they all had an update loop checking a bool in them

#

At least if its on singletons youre sure it wont be multiplied…

minor gale
#

you can also use this.enabled = false to disable the update loop

#

rather than the gameobject

hoary frost
#

Oh, i thought you couldnt disable scripts in the inspector? But you can programmatically?
That and, well, it also turns the script off entirely, so you cant recieve events, sync stuff or nothin

minor gale
#

they'll receive an inspector check box if you have an update loop, i'm not sure if that's the only case. it does not stop the script from receiving stimulus afaik

#

it may have sync implications

hoary frost
#

Then again its just me.
Im the freak taking hours setting up box colliders over every little thing instead of using mesh colliders like a normal person lol

minor gale
#

from what i remember if the script has started* it will still be capable of receiving data

hoary frost
#

Very interesting

#

Maybe it is free after all, worth an experiment

#

Such specific behavior to recieve a check box, cant say ive ever paid attention

timber parcel
#

I'm kinda enjoying the fact we have to code these games like we're running them on the performance of a PlayStation 1.

cyan hollow
#

Why does drinkManager.bartenderId not sync to remote players? It is marked as [UdonSynced] and DrinkManager is set to BehaviourSyncMode.Manual.

internal void SpawnDrink()
{
    Networking.SetOwner(Networking.LocalPlayer, gameObject);

    var drink = drinkPool.TryToSpawn();

    Networking.SetOwner(Networking.LocalPlayer, drink);

    var drinkManager = drink.GetComponent<DrinkManager>();
    drinkManager.bartenderId = Networking.LocalPlayer.playerId;
    drinkManager.RequestSerialization();
    drinkManager.OnDeserialization();
}
timber parcel
#

What part is not synced? The location?

cyan hollow
north thistle
cyan hollow
north thistle
#

Last time I checked, networked game objects don't have fully working networking for a period of time after they are first enabled.

A workaround is to never disabled networked game objects and to disabled their individual components instead (for example the renderer and collider)

#

Or you can have all the stuff that handled networking be in a parent object and everything you want to disable be a child of that parent object

cyan hollow
#

But I can try delaying the variable setting for a bit

north thistle
#

You might run into issues with networking edge cases, but yah it'll be a lot simpler and will probably work most of the time

barren scaffold
#

When checking playerobject ownership in unity runtime against a bot should it be returning you also as owner?

fallow mountain
barren scaffold
timber ferry
#

clientsim remote players don’t act like real players

#

they can’t hold any sort of persistent data

#

so i wouldn’t be surprised if the player object ownership didn’t work accurately with them either

#

i’ve noticed you can’t teleport them either, don’t know if that’s my code not working or clientsim remote players not being able to be teleported

#

and fun fact, trying to immobilize a clientsim remote player crashes your udonbehaviour

hoary frost
#

You have to ask that player to teleport themselves

#

And since clientsim remotes cant execute code on their own, they cant ever run it

cyan hollow
silver hill
#

Im having issues with Udon logic halting from working whenever a player joins the instance, it causes variables to change and things to stop acting as normal.

The logic isnt networked in any way at all so im wondering what on earth is going on

silver hill
#

Will do

silver hill
#

Alrighty sorry for late reply, here is whats happening, this issue causes a whole heap of other minor issues but this particular one causes people to not be able to complete the tutorial, they perceive the world as a buggy mes and I wanna jump off a 3 story building 😛

#

The value of 70 is being stored in this Udon which has no programmed way of changing outside of what its designed to do, I have no idea how a player joining is causing it to reset the value to 0

silver hill
minor gale
#

is this on a player object?

silver hill
#

No

minor gale
#

could you show the (presumably) player restore section

silver hill
minor gale
#

at a glance, your player restore there isn't branching based on the local player; it will fire locally any time someone joins. it also doesn't guarantee that your own player has restored initially upon your own join as it may fire another player prior to your own

silver hill
#

Uh?

#

Ohhh

#

so you're saying I need to check if its actually you loading in?

#

But... I always thought this would be just you, your local client loading in, why on earth would I want touse that node for whenever someone else joins? wouldnt you just use an on player joined node?

twin portal
#

OnPlayerRestored runs for for every player that joins. The output for OnPlayerRestored is the VRCPlayerApi corresponding to the event.
Your video conveniently cuts off which player you're loading data from, but you should be using the output of the event, and checking for isLocal when relevant.
So when a new player joins, It runs that function, and attempts to load the data from that player again. Most importantly, if those keys that you are requesting for are not present in that player's persistent data, then they will return the default value for the associated data type. Which is often 0.

silver hill
#

So like this then?

#

Or is there a more lite way to do it

minor gale
twin portal
# silver hill

even easier to just call isLocal in the VRCPlayerApi player, this outputs a bool if that player is also the local player or not. You can then plug this result directly into the Branch

silver hill
twin portal
#

plug player into that

silver hill
#

Oh yes my bad

#

Well that clears up a headache Ive had for weeks now

#

@twin portal@minor galeThank you very much for your time!

hoary frost
#

I can have a look at anything else tho

#

Either way looks like this was solved

#

Indeed onplayerrestored is a global event, you can sorta tell those because they have a vrcplayerapi output

#

There wouldnt be a need for such an output if it only fired for the local player 😌

glass lintel
#

quick question cause I cant find the answer on docs

Can I sync variables that aren't public using [UdonSynced]?

twin portal
#

yes

glass lintel
#

fire

severe forge
#

is it possible to network sync an instantiated prefab? the prefab has a vrchat pickup and object sync on it. for example pressing a button instantiates a ball prefab?

twin portal
severe forge
twin portal
#

you can do that, yes. but ObjectSync still won't work on it

severe forge
#

Ouch

#

This throws a wrench in my plans

#

So, no matter what, objectsync won't work on it unless the prefab is in the world already?

twin portal
#

pretty much, the only exception being Player Objects

severe forge
#

Player Objects?

twin portal
#

they work best for this specific use case, of course, when you need exactly one object for every player

#

if you need a copy of an object for every time you press a button, rather than for every player, you could instead use an Object Pool. It's technically not an infinite amount of objects, and the objects will still be in the scene at the start, just disabled, and the pool manages the enabling & disabling of the objects

#

all depends on your use case and why you need copies of this object

vapid pagoda
severe forge
gentle dove
#

What happens if a player requests ownership over an object, but the object owner doesn't send back a response because they are frozen or timing out? Is there some kind of automatic rejection or timeout?

twin portal
twin portal
onyx path
#

alright I feel like im bashing my head into the wall working with a coding helper to try to make something that can read audio data from a tv source like protv and move blend shapes with it, like an audio link of sorts, can this even be done before I sink more time into it?

tulip sphinx
#

audiolink is not some exclusive allowed script, you can check out what and how it does and do the same thing. and also thats not networking.

onyx path
twin portal
#

can't you just read the data from AudioLink and move blend shapes with that?

onyx path
frozen igloo
onyx path
#

using UdonSharp;
using UnityEngine;

public class AudioLinkAmplitudeReader : UdonSharpBehaviour
{
[Header("AudioLink Material")]
public Material audioLinkMaterial;

[Tooltip("0 = Full, 1 = Bass, 2 = Mid, 3 = Treble")]
public int amplitudeBand = 0;

[HideInInspector]
public float amplitude;

void Update()
{
    if (audioLinkMaterial == null) return;

    Vector4 data = audioLinkMaterial.GetVector("_AudioLinkData");

    amplitude = amplitudeBand switch
    {
        1 => data.y,
        2 => data.z,
        3 => data.w,
        _ => data.x
    };
}

}

frozen igloo
#

ah yeah, your problem is that copilot gave you nonsense

onyx path
#

ugh

frozen igloo
glass lintel
#

I may have already asked this before? I cant find it

If SendCustomNetworkedEvent has a limit of X calls per second, can I still, locally, call it more than X times per second? (sending to NetworkedEventTarget.Self)

minor gale
#

i can't imagine it rate limiting as it'd process the call immediately

glass lintel
#

sick

minor gale
#

at least i'd hope not, i'd find that odd

glass lintel
#

I have another question that has me stuck currently

Currently, I'm designing the game start system for my game, and there are several states it has to go through, some of these states are owner-only (only the owner runs them), and some of these states are synced (all players in game run them)

When the owner does a local state, and is moving to a local state, they should be able to continue instantly
When the owner does a local state, and is moving to a synced state, they need to tell everyone what state to move to, occasionally need to send data (might just use a synced variable, since order is guarenteed if I send it before), but, the owner should only be able to move to the next state when everyone responds

tl;dr, just wondering what would be a good method to do this, I don't need an exact solution

#

im also trying to do it in an organized way

minor gale
# glass lintel I have another question that has me stuck currently Currently, I'm designing th...

with a synced variable you don't necessarily send every variable

i.e. you change variable to 1, you request serialize

you change it to 2 within the call (or even on a subsequent frame) and request serialize

it's possible for only the 2 to go out because the serialization is just a request for it to send it out when it combs through and sees it flagged as wanting to serialize

this probably wouldn't happen in practice if you had a send/response before needing to shift the variable, but it is possible

i think it'd be best if players could infer the current game state at any point, in like an absolute sense (from an int or something), but that's not always possible if you do things sequentially/rely on previous state (and that is valid too)

i don't have a ton of practice making games, so i don't know what i'd do exactly, other than running through a central handler that ensures i'm enacting game state in a way that isn't reliant upon other objects/order

i think most vrc creators probably do not explicitly rely on responses for game starts, they have some owner/master player enact the game start and inform everyone that the game has started. if the master needs to pass out info/roles they'd likely just do that themselves and rely on data they can interpret on any eligible players (players who are in a trigger that opts them in for example)

maybe someone with more experience making games can provide better insight though

glass lintel
#

Trying to think of why I did this on the old platform

#

Because, I probably could have the owner do everything they need to do, then tell everyone to do what they need to do

#

yeah you know what, thinking about it, doing all this waiting is pretty pointless

minor gale
#

like i think it could be valid to process it through events on eligible players, but you could get into odd situations with an unresponsive player or leaver i think; if players advertised their preferences on variables it'd be possible to infer it at any point, but it may be a little harder to route around

glass lintel
#

I really could just have the owner do everything at the start, and then later, when done, tell everyone else do to everything else

glass lintel
#

alright sick, coding time

#

oh this is actually pretty easy since I should just be able to tell everyone "run game start, dont do [thing] if you arent the owner"

minor gale
#

are you making like a pvp game?

glass lintel
#

need everyone to locally ready the game, and need owner to randomize roles based off weights and assign them to everyone

#

My idea currently is to do it in 2 stages, the "Everyone do this to ready the game" and then "Alright, you have your roles now, do the rest"

north thistle
#

Personally I wouldn't fret too much about making sure everyone is there and good to go before starting a round and then assigning roles. Any cases of people dcing or freezing can happen just as easily after the role has been assigned and you'll probably want to handle it the same way anyways.

#

Also they can still freeze/dc right after confirming that they're there and ready to go, so you'll want to make a system that can handle that anyways

glass lintel
# north thistle Personally I wouldn't fret too much about making sure everyone is there and good...

Yeah, I was thinking that

I do still want to ping players before the game starts, because from previous experience (the old game on the old platform), its very common where a player freezes (eg. quest player AFK) for long periods of time before the game start- But, as for during-game start, im just gonna not care

i'm also going to try and make it less time-dependent, having a bunch of data where, if its already received by the time you get there, you continue, but if its not already received, you wait until you receive it, this way you can locally continue when you receive the data, rather than the owner waiting for confirmation from everyone and continuing to the next step

glass lintel
#

you're telling me

#

i can just

#

get when a player is suspended or not?

#

oh my god

#

goodbye entire ping system

minor gale
#

it can only happen to quest/mobile players i think, but yeah i think the server informs you of their suspended state

#

this is specifically for them like minimizing the app, not whether they're frozen

glass lintel
#

whoever worked on udon is my goat

minor gale
#

the master can also be suspended

glass lintel
timber parcel
#

does this look right for a network synced state?

My IDE is telling me I should be using a different style of method

glass lintel
#

had a question, while typing, found the solution, awesome

timber parcel
twin portal
#

Looks alright to me, my guess is it's Rider assuming you should do something a certain way that doesn't really work with Udon

glass lintel
#

another good one is not being able to do Array[^0] for the last item, got do Array[Array.Length - 1]

timber ferry
#

one reason why i stopped using rider soon after i tried it out

#

i know you can probably disable it, but it felt like it handholds way too much

#

and it doesn't understand udon, so the suggestions don't even make sense every time

faint rock
#

You can disable specific recommendations for your project so I just do that on an ad-hoc basis when I find it recommend something VRC doesn't support.

north thistle
#

"where All contains Self"
do you mean when the instance only has a single player?

#

You're still using 1 event usage per All regardless?

#

VRC isn't pure p2p, it uses a proxy server...

#

You send the event once to the proxy server and the proxy server splits it to everyone else in the instance

minor gale
#

event splitting will not split the event into multiple events, it's referring to splitting and reconstructing the data if it's large enough to warrant fragmenting it

#

it does note that the split will affect your rate limits internally though, which is interesting to note at least

north thistle
#

Where are you getting this info, Ophura?

north thistle
#

This sentence is very confusing to me:

"when invoking a custom networked event with an event target of All; the event will run at least once for each player in the instance, which eventually executes for the Self player; this invocation won't be affected by any rate-limits (neither set by NetworkCallable nor internal rate-limits)."

It seems to claim that events running on remote clients will count against the local rate limit.

#

Well just to clarify it for everyone:

Self has no rate-limit cost

All has a rate-limit cost of 1 (before Event Splitting is applied)

Owner almost certainly has an identical rare-limit cost to All when the owner is another player; it most probably is identical to Self when the owner is local, but nothing in the docs makes that explicit

glass lintel
#

So, if the local player sends to All 10 times, and the rate limit is 5/s, the local player will see all 10, and the remote players will see 10 over a second? (5 'instantly', the other 5 over 1 second?)

#

And then, when sending to owner, if you ARE NOT the owner, it counts towards rate limit, but if you ARE the owner, it does not count towards rate limit?

north thistle
glass lintel
#

got it, sick

#

about what i expected, if i ever run into unexpected behaivor ill test it

minor gale
#

i actually wasn't sure if it'd queue them when calling all, it calls all 10 instantly (locally) when i tested just now and remote received them over time

north thistle
#

Also just now trying it out in Build & Test, I can confirm that when the target is Owner and the owner is the local player, it does not appear to contribute to any rate-limiting

#

for the owner of the object spamming the event:

#

for the non-owner spamming the same event:

dim sentinel
#

Hiii

warm steeple
#

I am storing a list of player ids in a DataList that I serialize via json over the network but for some reason when I try to check if the id of a player is in there (using Contains()) it always returns false even though the id is indeed in the list, what's up with this?

#

testing locally in a vrc client

#

for the owner it works fine but not for others

warm steeple
#

bruh, turns out owner had ints and others doubles

#

I need to remember to explicitly cast when deserializing...

warm steeple
#

feels so nice when you realize how to fix this stuff and it works

heavy spindle
#

I love network encoding

oak rivet
#

Trying to call a simple networked event, but setting up the event causes this error to appear, and when trying to manually enter the networking library, it returns another error that it doesn't exist. Any idea of what to do?

minor gale
oak rivet
#

My IDE doesn't seem to recognize that it exists?

minor gale
#

are you on 3.8.2? (the sdk)

oak rivet
minor gale
#

in the VCC you can check in Manage Project

#

you should close your project if you try to upgrade the sdk

oak rivet
#

It was on 3.7.6, upgrading rn

minor gale
#

that seems pretty recent, but i don't remember exactly which version added the new network events

oak rivet
#

Still returning the same error...

#

Found the problem

#

I set the script to automatically update instead of manually

#

Who would've thought

#

Thanks for the help though

#

What type of arrays can be synced using manual events?
I was told that only Byte Arrays can be synced, but looking online doesn't confirm this at all.

minor gale
#

byte arrays are the only array type in playerdata persistence, but that doesn't extend to playerobject persistence

weak mason
#

so i finally got dropdowns to play an animation, now how do i go about makeing it sync for players and late joiners

fallow mountain
# weak mason so i finally got dropdowns to play an animation, now how do i go about makeing i...
  1. you have a synced variable that is going to sync e.g. DropdownAValue
  2. Owner change DropdownAValue (if not owner, set owner prior)
  3. Owner request serialization, and then send custom event DropdownAUpdateAnimationPlay
  4. You need to have a OnDeserialization node (for non-owners) to send custom event DropdownAUpdateAnimationPlay
  5. Make the DropdownAUpdateAnimationPlay use DropdownAValue to update the dropdown and do animation
lost tapir
#

Hello! I have been trying to learn how to make a UI menu that allows players to flip through pages of options for my world. I cannot find any tutorial that shows how to setup such scripting. I prefer to use Udon Graph. Any help would be so greatly appreciated - Please reply to this message so it pings me >w<

twin portal
#

the bits that look like this

lost tapir
#

Feel like I am missing something small

twin portal
#

you loop through the entire array of Pages, enabling all GameObjects in the array with this Interact event

#

this won't cycle one at a time

lost tapir
twin portal
#

though cycling doesn't make sense if you have a dedicated button for every page

#

it would be easier to have the button first disable all pages, then enable the page the button is for

#

so for the button on Btn_Credit, Interact > disable all GameObjects in the Pages array > enable the current page

lost tapir
twin portal
#

uncheck the box in SetActive
then make another SetActive node, and connect it to the Exit of the For loop
The "instance" you'd plug into this second node should be the page you want to enable

twin portal
#

with the currentPage variable being public

#

you could also have just one script to handle it all rather than having a copy for every button. either works

lost tapir
twin portal
#

looks fine. check your console for any errors, see if the script is crashing

lost tapir
#

So theyre all turning on as intended, but no off function

twin portal
#

is the little checkbox in the SetActive nodes the correct value?

#

should be unchecked to disable, checked to enable

lost tapir
#

Omg i dont have a noodle on one

twin portal
#

you have the nodes set backwards compared to my example

#

the SetActive connected to Body should be false, the one you have now connected to Ex it should be true

lost tapir
#

It works

#

Thank you so so so so much!!!

twin portal
#

no problem

peak shard
#

So I'm trying to make a teleporter pad that plays an animation for everyone during the teleport, that DOESN'T teleport everyone in the instance. How can this be changed to fix that?

twin portal
# peak shard So I'm trying to make a teleporter pad that plays an animation for everyone duri...

OnPlayerTriggerEnter/Exit will also fire when a remote player collides with it.
So if you watch another player touch the trigger, it will also fire on your end as well.

If you only want the player that touched it to run the event, check if the colliding player is also the Local Player; you can get what player caused the event with the VRCPlayerApi player output of the OnPlayerTriggerEnter/Exit nodes

peak shard
#

Where do I plug that into?

#

I don't know where to plug these into.

twin portal
#

just plug it into a Branch

#

then connect TriggerEnter - > Branch -> true part branch -> the rest of your code

peak shard
#

And that should be all I need to fix it?

twin portal
#

yes, that will make it so the code will only run if the local player touches it

peak shard
#

Now the animation plays only locally and it doesn't teleport anyone.

peak shard
#

I'm giving up on trying to delay the teleport using a script. Will this be local or will this teleport everyone?

twin portal
#

this would request all players that witness the TriggerEnter to teleport the player that touched the trigger
Which I think... should work? Since only the local player can teleport themselves

thorn valve
#

hi ✨
may i ask for some help with udon and network please 🤍
i just started to work on my first world project, maybe it’s a month at this point. A lot of things are done, but networking makes me crazy. no google or ai can give me answer i’m sure about.
Huge part of my world is collaboration - anyone can change variables or what ever and everyone must be in sync. And i just don’t get it, how to make robust system to even pass 1 variable if a lot of people try to interact at the same time. setowner, success no success, is there a que or no que… so could anyone explain to me what will be the proper way to make just one robust synced slider, which anyone can move around and keep everyone in sync? my code flow is like this:

  • onsliderchanged fires
  • small throttle for only 10 times a second
  • if enough time passed
  • setOwner
  • wait if owner or not. if not return and wait for ownershipTransfered and wait that it’s me
  • if an owner or got ownership - update synced variable from local one, request serialization
  • on post serialization if success apply changes to yourself if not, just dump changes.
  • on deserialization others update the synced value of the slider using setWithoutNotify

But, is setOwner instantaneous? I can’t find how it works at all. will it wait for ownership for 10 years? what if i drag slider non stop and cause 10 changes a second, are changes qued? do i even need postDeserialization? what if i have two people requesting ownership at the same time? changes qued by time order or only one gets chance to sync and other who tried at the same time got nothing?

i will be happy to get any information. these small things makes me crazy because i need some how robust system where i understand when it fails and can decide it’s fine, but i just dont understand how much i have to do for protection or maybe data just dissapears and behave not the way i though and now people are desynced T__T

#

i have much more things done with network, but example with slider would help me a lot, it’s simple but gives kind of a perfect example.

twin portal
# thorn valve hi ✨ may i ask for some help with udon and network please 🤍 i just started to w...
  • SetOwner is instant. As soon as a player sets themselves as the Owner, they can immediately edit synced variables and RequestSerialization.
    The docs detail additional methods to actually negotiate ownership, but this is optional. "Waiting for ownership" is not something you are required to do.
  • How this data gets synced depends on what sync type your script is using. If you're syncing a slider's value for example, Continuous sync will sync the value at any time when the value is updated, with some interpolation. Manual sync will only send the values to other players if you call RequestSerialization; then whatever the other players need to do with this data (like set the slider's position, etc.) you would run in OnDeserialization. OnDeserialization does not run for the sender, however, so you need to either do the setting immediately after you call RequestSerialization, or call the functions in OnPostSerialization.
  • If multiple players are requesting ownership simultaneously, they'll fight over it, and the server decides who wins. Anyone who loses will have their data ignored, so you generally want to avoid situations where you are changing ownership frequently.
thorn valve
#

ok, so setOwner is instant, got it.
my mode is manual and i do take care of updating for myself and others as you said, so i guess that one is fine.
i don’t want to fire ownership hell, but i would prefer to understand what will happen. So, i will get ownership, then i’m allowed to call serialization right away, but will it be synced or not depends on latency and what ever chance - only one right?

#

and stupid question. i do scripts, should i use udon graph or does not matter - what ever i prefer?

twin portal
#

pretty much up to your own preference, they both have their pros and cons

#

whichever makes more sense to you

thorn valve
#

and, what about ownership transfer and postSerialization? those were mentioned to me as important so i never push buffered changes before get approval that its fine

#

So if i want to change a variable or a vector i have a sequence where i buffer my chnages, then try to get ownership, if not - wait for ownership transferd. when that one fires and i’m the owner - i call serialization but do nothing yet. only when post fires i check if it was done and apply to myself buffered changes - is this stupid, not needed?

twin portal
#

I wouldn't call it stupid, but as I said, waiting to confirm ownership isn't necessary

thorn valve
#

do people wait for post? to see if it went through? what if it failed and i applied changes to myself, which others did not get it?

twin portal
#

curious how you're "waiting" though. Are you just using OnOwnershipTransferred?

thorn valve
twin portal
thorn valve
#

and drops if after one second i did not get ownership

thorn valve
#

so, basic proper way to sync with people then looks like this:

  • get new variable state you want to sync
  • buffer it to network synced variable, do not apply to yourself yet
  • setOwnership
  • do not wait, request serialization right away
  • wait for OnPostSerialization
  • if done - call function which uses synced buffer variable to apply local changes
  • other players OnDesirialization - use same function as master to pull synced variable to local

this way we know that we transferred data and we have just one function which apply buffered/synced variable to local for both slave and master

#

something like that?

twin portal
#

setting the variable and setOwnership needs to be the other way around; you have to be the owner before you can edit synced variables

safe path
#

Legos, I think I remember you saying that on trigger enter and on trigger exit run for everybody is that right? In that case I’ll just make them run for the local player.

thorn valve
#

wait, i thiough you can put inside synced variable what ever you want, it’s just never synced if you dont call serialization

#

btw, i dont want to bother anyone too much - are there well known projects on git or whatever which i can use as “well known good implementation” examples? So i can go and read and not spam here :3

twin portal
#

nobody writes good code

thorn valve
twin portal
#

in seriousness the only thing I can think of is some of the VRChat examples

glass lintel
#

being a programmer is coming back to your code a week later and saying "this code sucks"

twin portal
#

you could always look at the code of a random prefab, but those tend to be very complex and hard to follow

#

if you're trying to learn something simple from them, that is

thorn valve
twin portal
#

I can give you all of the code I used for Infinium if you want.

#

the networking for that crap was surprisingly never a problem

thorn valve
#

can we get one :3, but it’s also fine, i think i’ve got all answers i need ✨

#

thanks a lot for the help ✨

twin portal
#

maybe I can find a small one.... lol

#

exactly my point that it's multiple scripts all working together, so just one by itself may not make any sense and not have anything useful to learn from

thorn valve
#

it’s ok, i’m already happy with info :3

twin portal
#

rather than give the code, I've now realized why the networking was never a problem: I never change ownership of anything

#

Infinium runs mainly on PlayerObjects, so I just take advantage of that, and additionally the game doesn't really have any reasons to change ownership

#

just sort of further showing that it's better to not transfer ownership if you can help it, I guess. If you need multiple players to be able to adjust a value frequently, instead of changing owner, you can just send an event to the current Owner to do the calculating, rather than fight over ownership

#

I used this in a demo to make a value that can be updated by anyone, and updates quickly without data loss

thorn valve
#

unfortunately i cant. some parts where you hold an object - yes, those are fine. but i have a control timeline for setting frames and time and adding edits and that one i cant separate. and if everyone wants to jump to the same frame, or some one else added a frame or changed timing - it’s like working on project all-together in powerpoint or what ever similar like devinci

#

i have small values to take care of here and there and slider is the one, where if people decided to work in synced - anyone should be able to pull it and force all other people to jump on the same time slot

twin portal
#

you still wouldn't have to change owner for that

#

you can have one player handling setting the values, made especially easy with the netowork events with parameters now

thorn valve
#

i mean i can send to master and then to everyone

twin portal
#

master is not guaranteed to be the owner of the script

#

but that's the idea essentially

#

send to one player, that player sends to everyone else

thorn valve
#

but… how to guaranty everyone gets the event and stay in sync?

twin portal
#

same way it would sync normally. Only by doing this, you're not fighting over ownership anymore

thorn valve
#

possible lost of data?

twin portal
#

not anymore, since ownership isn't being changed.

thorn valve
#

i mean are events as save as serialization?

twin portal
#

yes, both are referred to as "eventually reliable"; if you know they've been sent, they will, eventually, arrive to their target(s)

#

but are almost always immediate.

thorn valve
#

what you are using as synced variables then? what would be for sure not a custom event?
i’m actually supa curious now how furality and club events are taking care of network

#

there is probably only one owner and everything is supa strict, but still.

#

synced events smell with checksums and version number of a change now :3

#

a bit more complex, but if it works… maybe i should used it

twin portal
#

you're still using synced variables, just only one player is allowed to edit the "real" ones and send them out to other players

thorn valve
#

ohhhh

#

hmmm

#

so if not an owner send custom event if an owner do serialization - sounds good

#

i feel stupid i didn’t thought about that

twin portal
#

yes, you SendCustomEvent, target the owner, and can even include the value you want as part of the event's parameters

thorn valve
#

i need to pull ping to server and make owner with fastest connection :3

twin portal
#

that's usually already figured out via Master

thorn valve
twin portal
#

VRChat does a bit of calculating to decide the best player to become the Master, if it's been transferred

thorn valve
#

so, i just need a check if master or not and that is it?

#

i dont even have to select?

twin portal
#

no, don't check for master, check for the owner; they will often be the same player, but it's safer to use Owner.

thorn valve
#

is there a function send custom event to an owner or just to everyone and only owner does the job?

twin portal
#

yes, the SendCustomNetworkEvent allows you to specify the targets, and one of the options is Owner

thorn valve
#

ok, small refactor it is :3

twin portal
#

basically:
Player1 is the Owner of the slider. All other players, inherently, are not.

  1. Any player updates the slider (including Player1). The slider calls SendCustomNetworkEvent, targeting the Owner, and sends along the value of the slider as a parameter.
  • basically "Hey Owner, I want to change the slider's value to X now."
  1. Owner receives the NetworkEvent. Takes the value from the parameter and sets it to the synced slider variable. Owner also requests serialization.
  2. Handle the network update as usual; other players receive the changes in OnDeserialization, the sender makes changes in OnPostSerialization
#

so if 10 players change the slider at the same time, they'll all go through the Owner instead of being lost in the process of ownership transfer

thorn valve
#

sounds ok, latency tho… hmmm, and how to buffer changes too.

#

because i want to move slider, and it should move nicely for me and not being affected by owner telling me onDeserealization

#

i guess i need to find a way for that

twin portal
#

that may not end up being a problem, I'd test it first
but there are also some features with NetworkEvents where you can limit how many events are being sent per second, and can add some logic so that you don't send more events if there's already too many waiting to be sent

thorn valve
#

i have limits for updates already. i do want to get smooth transitions but it’s probably impossible, so it’s ok.

twin portal
#

probably some magic you can do with lerping to make it transition nicely

thorn valve
#

ok, i guess there is no way around it. ether get owner fights or one owner controlling everyone

#

lepring?

#

interpolating?

twin portal
#

yeah

thorn valve
#

i can get speed of slider change and transfer speed and end point

#

ok

#

i was thinking about that but lets see will implement xD

#

it will look much better, but there is a chance i will fuck up myself… ohhh, wait, i wont, i have slider as a separate object now, so i just need time output and that is it.

#

hmmmm

#

or maybe not… i will think about it.

#

thanks again ✨

warm steeple
#

interesting read, I also came to the conclusion that it's more straightforward to ask the owner for the changes others wanna make. there will be latency but for my use case it doesn't matter

#

if something like a slider is used one could just set the value locally and ask the owner to change it as well, while the slider is being held buffer the change of synced value received from the owner and when the slider is released make the update on the player. that way it stays in sync and the network updates won't be sporadically changing the slider while you're editing it

silent kite
#

really basic question, but for the IsOpen14 variable, does anyone know how i get the sync changed from none to automatic from here? followed the steps from a YT video on how to create doors, and it said that this was good for global settings, but after digging around, i found the sync was still set to none, when i believe it should be automatic.

minor gale
minor gale
warm steeple
#

idk actually

#

haven't thought about it lol

minor gale
#

i think you'd just have to infer it from the value being changed recently via events, i think there are real ways to do it that aren't possible in udon

warm steeple
#

maybe via this or smth

silent kite
#

much appreciated

minor gale
silent kite
#

thats what i am looking around for right now, attempting to find the Set OS on Int checkbox -_- unfortunately in this first year of unity, i have found that apparently no 2 people run the same version of unity, and every guide you find has the same final house being built, but is using completely different building materials so to speak...

#

and after doing some more investigating, it seems the option might not be available in my version..

warm steeple
#

that's odd, that method is exposed

#

oh right, that makes sense

timber ferry
#

first of all, if you want to detect when a UI element is held, you have to use the EventTrigger component, we cannot use OnMouseUp and OnMouseDown directly in udon. however, note that due to a bug, these events will fire twice in the same frame, so you need to account for that (related conversation here: #world-ui-design message)

second of all, why do you have a VRCUiShape AND collider on your handle? that is completely redundant. what the VRCUiShape does, is add a collider to your canvas if it doesn't have one, and allows for focus view on mobile. that's it. the only part of your UI that needs a collider is the root object with the canvas component, and you can either add one yourself or let VRCUiShape do it for you. do not add colliders or VRCUiShape components to any UI elements that are a child of your canvas

#

also, you'll want to use OnPointerUp and OnPointerDown, not the Mouse variant. i believe this will also account for VR

minor gale
#

i'm getting good results with the OnDrag events (all 3) unless there's some issue i'm unaware of with them

glass lintel
#

Is it possible to send an int[][] with SendCustomNetworkEvent?

#

searching for it further, because discord search sucks, looks like it does not

#

time to play with OnPreSerialization()

north thistle
glass lintel
#

its supposed to be object data for custom maps, assuming players can only use the current GenericMapObject, its about 13 bytes per object

#

im fine having it save as json, my idea for sending it over the net is just turning the byte[][] into byte[], with the first item of the second array containing the length of the array

#

(working on 2 projects currently, this is unrelated to the other one, one im working on as a solo side project)

minor gale
#

if it's an int[][] it's probably even easier to pack into a single int[] array, like deserializing it could be the outer length, then the inner length, followed by every element for that inner array, repeat for each inner array

#

though if you expect using other types it'd be better to pack to bytes i think

glass lintel
#

theres gonna be 2 types, the json that can get exported/imported, easy to read and manipulate, and then the data that gets stored/synced, which is a very compact and optimized byte[]

#

either way, when you import the json, it just gets stored/synced as the byte[]

#

its mostly for me lol, the preset maps for the game are going to be made with the same map editor i'm giving to the players

glass lintel
#

i just realised i dont really need to make my own json format thing lmao, I should just be able to create and export a data dictionary

humble topaz
#

Is there a way to get this ping value? I tried VRC.SDK3.NetworkStats.GroupDelay(owner) or other values of the Stats-class. But I can't figure it out - or at least no value resembles this ping value 🤔

I try to predict a manual synced position, so that two players, each riding their dragon, can fly next to each other. If I set "latency" manually to the value of the ping, they fly on exact the same position in both instances (maybe only a coincidence?).

float latency = VRC.SDK3.Network.Stats.GroupDelay(owner); // <-- this value is too high - but ping would fit

// Calculate the time since the last deserialization
float deltaTime = Time.time - lastDeserializationTime;

// Predict position based on last known velocity
Vector3 predictedPosition = lastDeserializedPosition + velocity * (deltaTime + latency);

// Smoothly move to the predicted position
transform.position = Vector3.SmoothDamp(transform.position, predictedPosition, ref smoothDampVelocity, smoothTime);

(velocity is calculated in last OnDeserialize event)

frozen igloo
humble topaz
finite sierra
#

Pretty sure we can only do primitives

#

Like bytes. Strings . Longs etc

twin portal
#

you can sync these, and arrays of these types

cyan hollow
#

What's the proper way to go about this:

  • Someone takes ownership of a GameManager script and changes a synced value.
  • Different people own different Player objects, each that reference that synced value in their own OnDeserialization.

I currently try:

foreach (var player in players)
{
    player.SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(Player.Serialize));
}

in GameManager.OnDeserialization, but it doesn't seem to properly work.

Player.Serialize is the following:

public void Serialize()
{
    if (!NetworkCalling.CallingPlayer.IsOwner(gameManager.gameObject)) return;

    RequestSerialization();
    OnDeserialization();
}

Regardless, this seems like the wrong way to go about it. But (I think) I need Player.OnDeserialization to reference that synced value from GameManager because it enables something locally based on it and a synced value from Player itself.

tulip sphinx
#

wha

#

gamemanager script receives ondeserialization event locally

#

now everyone knows and can reference that value

cyan hollow
tulip sphinx
#

huh. make a player an owner of a current question, what

#

what is even player.serialize

cyan hollow
# tulip sphinx what is even player.serialize

Player.Serialize is the following:

public void Serialize()
{
    if (!NetworkCalling.CallingPlayer.IsOwner(gameManager.gameObject)) return;

    RequestSerialization();
    OnDeserialization();
}

In my original message

cyan hollow
tulip sphinx
#

onchange?

#

or ondeserialization and then compare old value to a new one

cyan hollow
tulip sphinx
#

ondeserialization -> targetUdonBehaviour.SetProgramVariable("VariableName", newValue);
given othet script had on change, or to be sure just fire custom event after it as well

cyan hollow
tulip sphinx
#

what

#

tf is player then

cyan hollow
cyan hollow
#

Perhaps that's leading to the confusion

#

(although if a word is in a code block, it's probably code or a class/field name)

tulip sphinx
#

ye, comprehensive naming my beloved. what to you mean by ondeserialization being called when x, it gets called only on deserialization of its own script, if you need same to happen on other conditions than add some other way for the same function to be called

cyan hollow
#

Why does this not work?

GameManager.cs

public override void OnDeserialization()
{
    foreach (var player in players)
    {
        player.SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(Player.Serialize));
    }
}

Player.cs

public void Serialize()
{
    if (!NetworkCalling.CallingPlayer.IsOwner(gameManager.gameObject)) return;

    RequestSerialization();
    OnDeserialization();
}
#

Hopefully this should make my question clear(er)?

tulip sphinx
#

you csnnot call ondeserialization

#

k i get it

#

just make ondeserialization: do my stuff

#

on custom event 'Serialialze:' do my stuff

#

do my stuff being custom event/function whatever you call it

cyan hollow
tulip sphinx
#

you cannot explicitly call random events

#

only custom events

cyan hollow
#

But I'm explicitly calling RequestSerialization. Why doesn't it work? Because no synced data was actually changed?

tulip sphinx
#

normal hardoded events happen only when normal hardcoded stuff happend

#

oh

#

anyway, you def csnnot call
Ondeserialization();

cyan hollow
#

Out of (bad) habit, I call both RequestSerialization and OnDeserialization, but the main question here is where does the issue with my code lie:
in

foreach (var player in players)
{
    player.SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(Player.Serialize));
}

or

public void Serialize()
{
    if (!NetworkCalling.CallingPlayer.IsOwner(gameManager.gameObject)) return;

    RequestSerialization();
}
tulip sphinx
#

ie you can only react to it, not invoke it other way than request serialization

lone zealot
#

Why do you even use NetworkCalling.CallingPlayer.IsOwner?

cyan hollow
lone zealot
#

No but the standard function to call there is just Networking.IsOwner

cyan hollow
lone zealot
#

Hmm in any case, if youre not actually serializing anything, then it wont ever run OnDeserialize

#

so yeah you have to change some synced var

cyan hollow
lone zealot
#

yes

tulip sphinx
#

i think request serialization does implicilty send all vars to remote users

twin portal
#

why are you requesting serialization if you have nothing to sync?

#

what's the point?

lone zealot
#

RequestSerialization sets a flag, that the next time the networking handler looks at the object it serializes all data.

#

Ohh actually

#

you dont need to change anything, but you have to have at least 1 synced var

cyan hollow
cyan hollow
tulip sphinx
#

owner gets no ondeserialization at all. other users do. then both run some custom event (function) based on that, thats gist of it

lone zealot
#

Ohh

#

dont call your script Player

#

Unity has some builtin stuff that collides with it

lone zealot
#

Thats not what Im referring to

cyan hollow
lone zealot
#

Hmm good

cyan hollow
# lone zealot Hmm good

lol yup. But yeah, as a rule, don't name your scripts the same thing as other scripts without namespaces lol

lone zealot
#

Well yeah I mean that should be obvious, but what isnt obvious is that Unity has builtin things like Player

#

Ohh

lone zealot
#

Youre also calling OnDeserialization on the Player instance

cyan hollow
lone zealot
#

yeah then you need a reference

cyan hollow
#

I'm trying to call Player.OnDeserialization.

cyan hollow
#

I think we're going backwards here 😅

cyan hollow
#

The solution was extracting logic into a function that both OnDeserialize AND this networked event call. I guess RequestSerialization only calls OnDeserialize if data changed? Or maybe something else was happening. Regardless, it works now.

Thanks y'all.

hoary frost
cyan hollow
wintry bear
#

What are some of the recommended paradigms for a dynamic number of synced objects per player that aren't exclusively ownable by that player?

I'm working on a project where each player would start with 2 objects that need to be network synced for position/rotation, and able to be picked up by anyone. What these objects are would be chosen from among maybe ~10 different types of objects, a player could double up on a single type of object, and there could be up to ~32 players.

The need for others' to be able to pick up and take ownership of the objects naturally rules out PlayerObjects. The most straightforward solution I can think of right now would be to use multiple VRC Object Pools for each object type with a pool of up to the maximum number there could exist at a given point based on the max player count, together with a VRC Object Sync on each object. But that would mean a minimum of 8 * 32 + 2 * 64 = 384 pooled objects, which feels like a lot when the absolute maximum number of spawned objects at a given time would be 32 * 2 = 64.

Ideally it would be nice if there were a way to dynamically allocate network ids to objects as the player count goes up, but outside of PlayerObjects I don't believe that to be possible. I could also theoretically write my own networking layer to handle object movements especially since they would be moving rather infrequently otherwise (or have situations/states that would avoid the need to sync them at times), but that sounds like it would be prone to syncing issues, slower to update, and a pain to write and manage in general.

tulip sphinx
#

uh, just have 64 vrc sync objects and control their properties (type etc) externally via manual sync

wintry bear
#

Just a generic object that can exhibit the different object properties? That had come to mind, but I hadn't looked into the feasibility of it yet with the other states the object could have

tulip sphinx
#

like i have a rather buggy due to my limited knowledge back then but mostly working world of 200 pickups each can be anything from like 70 types with different meshes, colliders, scripts, interactions etc

wintry bear
#

Perfect, I'll look into that path further then, thanks!

tulip sphinx
#

@wintry bear also as a headsup, with pool there are always problems of spawning in right position and need for delay on that with owner change etc. so since vrcsync sleeps and has no networking cost being idle, id suggest to just go with vrcsync object being empty/invisible when not needed, rather than trying to use pool and ever disabling them

wintry bear
#

Good to know. I'm not terribly experienced with either so that saves me some time and headache learning the hard way 😅 I'll see about make a collection of generic synced objects that are assigned their properties when activated and placed with whom they belong.

#

Thanks again @tulip sphinx 🙂

north thistle
#

Also yah, if you're pooling networked objects and doing more advanced stuff them with them, I'd advise making your own pool instead of using VRC Object Pool. VRC Object Pool disables the entire GameObject of the items in its pool and disabled/newly enabled networked objects tend to have undesirable behavior. You'll want to make a pool that "disables" them in a way that isn't via disabling the entire GameObject.

finite sierra
wintry bear
minor gale
#

i wouldn't say it does nothing, you can support an arbitrary amount of networked objects if you run it yourself, but it's not trivial

pooling doesn't work super well at some scale (like if you were doing an rpg/crafting system)

finite sierra
minor gale
#

you'd still use vrc's networking for passing data, there's not much way around that; i use network events in my own example. you have to define what can be spawned and how to pass data about those objects. supporting late joins is more involved, but it is possible

i think in prismic's case i'd probably define a way to instantiate the visuals on an object when the synced type changes, with a pooled object as the base

vapid pagoda
#

i highly recommend nobody should try to create a spawn system with late joiner support, speaking from experience (but its also fun)

cyan hollow
tulip sphinx
#

you def dont need every object to have every thing. you only care about network ids on vrc sync, since cant duplicate those, the rest can be easily instantiated from a single (for each kind of item) prototype.

north thistle
finite sierra
#

Like my own world i have a little over 250+ networked objects when the instance is full at 48 people and its all manual sync. No issues there either

#

With another 100+ being added soon or later.

wintry bear
#

Follow up question: I assume VRCObjectSync won't synchronize it's transform's parent changes from the owner? In other words, if I have the owner move the object from one parent to another, it will still be under the original parent to others unless I also perform the same reparenting action locally for them?

wintry bear
#

Thanks, figured as much but wanted to verify before I went down the route of accounting for that.

warm steeple
#

speaking of VRCObjectSync, if I have an UdonBehavior on the same object and its set to manual, does VRCObjectSync also become manual and only syncs when I do RequestSerialization()?

warm steeple
#

wtf is this

minor gale
#

you might be trying to do something related to ownership on a player object, they specifically have fixed views (ownership)

warm steeple
#

oh so that's what a fixed view means

tulip sphinx
#

so, all and all, you dont place other networked scripts on the same object as vrcsync

warm steeple
#

got it

wintry bear
# tulip sphinx so, all and all, you dont place other networked scripts on the same object as vr...

Becomes a bit of a nuisance though when you want to ObjectSync a Pickup, since then you also need those on the same object as the Rigidbody and Collider, which is where you also have to have the Udon script if you want to detect the pickup/interaction events and do anything network related with them. As far as I'm aware it means if I want to manual sync any related data I have to create another object to hold the manual sync udon script and forward the pickup/interact events to it.

tulip sphinx
#

@wintry bear sure, dummy functions on non-networked script on pickup itself and child object with manual sync to receive them and do the rest.

#

iirc my hierarchy was vrcsync - manual sync - intance of prototype object. i also read collider types on prototype and applied them to existing colliders (like, disable cube, enable capsule, set length/radius) on vrcsync object tho now when i think about it, probly couldve just copy the whole component or smth

wintry bear
#

More or less what I'm implementing now. The amount of extra data I am syncing is fairly small so mostly worked fine with continuous sync on the same object, but because the instance master is also holding ownership of all the other unowned items, it can be enough to network clog their continuous sync. The data changes infrequently enough I'm gonna just offload it to a manual sync on a separate object.

tulip sphinx
#

i tried continuous sync at first, wasnt good for my goal of 200 and again, it can break with 'vrcsync type of sync' taking over

#

i hand only 2 shorts and 2 bools synced so i think it was more about the latter than the amount of data. but i didnt knew back then

wintry bear
wintry bear
tulip sphinx
#

hm🤔 . use overlay for network objects, idle pickups should go to sleep in a few seconds normally

wintry bear
#

Which overlay?

tulip sphinx
#

i dont remember, the one that not a console but floating plaques near networked objects

wintry bear
#

Right, I think that's one of the higher ones. Yeah, I need to check there and see what exactly is going on with them.

frozen igloo
wintry bear
wintry bear
# frozen igloo setkinematic on the objectsync, not the rigidbody. When you have objectsync, it ...

Nope, even when using ObjectSync's SetKinematic, and even checking the overlay to verify the other objects are properly asleep, it's still too much.

With 64 objects (4 of which are in use, the other 60 are kinematic and asleep as confirmed by the debug console), the overhead still seems high enough that interactions from the instance master behave as though clogged. If other players interact with an object such as throwing it, the instance master sees the changes update quickly as expected. But if the instance master interacts by throwing the object, the remote player sees the updates occur roughly once every half second or so, jumping positions without the simulation.

To confirm this is tied to the number of objects, when I reduce the number of networked objects to 4 in the build and change nothing else (basically just removing the objects that weren't being used and left asleep in the background), the problem goes away. Both the instance master and the other player see objects behaving as you would expect.

Guess it's time to do more digging, maybe experiment with disabling those unused objects instead.

minor gale
wintry bear
minor gale
#

if you're running continuous vars on a script at the same level as object sync, the sleep doesn't really work the same way

i'll have to test it real quick, but from what i remember it will continue to advertise the object, but remote players will not deserialize them

wintry bear
minor gale
#

continuous itself just broadcasts continuously, object sync does some specific thing that's sort of continuous unless sleeping, but mixing them hasn't worked well in my experience

wintry bear
#

Does changing the owner of a parent object like a pickup apply to a child object's udon behavior as well, or will I need to trigger an ownership change on the behavior manually?

minor gale
#

you'll need to do it manually, you could listen for the ownership transfer event on the parent object to handle it more implicitly though if that's desirable

hybrid kindle
#

Did vrc change the way ObjectSync Respawn works? Testing using ClientSim and the console says its teleporting but the object itself doesn't respawn.

#

I think my problem is I want it to override respawn when a player is still holding it.

wintry bear
#

Reporting back that splitting the synchronized udon behavior to it's own object for manual sync has solved the clogging issues 🙂

fringe creek
#

Any chance there's an annotation or something that can be added to a method so that I can avoid adding the following code repeatedly to the beginning of some methods on synced objects?

if (!Networking.IsOwner(gameObject))
{
    return;
}
#

Actually perhaps I need to be sending events to just object owners more often

warm steeple
#

I made it one line so it clogs the file less

#

pretty much the same as an annotation

fringe creek
#

I wonder if Udon allows for custom annotations, might be helpful to represent the scope of an event listener. Naturally just calling the events on the owned objects would be the same thing but it's good practice to have the method itself indicate it's scope so other devs don't have to wonder about side effects, etc.

finite sierra
fringe creek
finite sierra
frozen igloo
# fringe creek Yeah looks like I meant "attributes", I was thinking annotations in a java sense...

Attributes are a way to add additional functionality to events, fields, or classes. It sounds like you're looking for XML comments. You can start an XML comment by typing out /// before an event and if your IDE is set up correctly, it should automatically add the <summary> section and also sections for each of the variables for the events. Inside of those sections you can put your comments, and then when you hover over that event somewhere else it should show you a pop-up with the information you filled in

fringe creek
warm steeple
finite sierra
sturdy hornet
#

if someone is the owner of an object or needs to send data to players that currently have a player blocked will it not send out the data?

twin portal
#

no, the data will still send

sturdy hornet
#

okay cool slay

warm steeple
#

can I change ownership of objects with vrc object sync? for some reason when I do this on my object with AI agent it teleports close to the destination

#

I do transform.position = Agent.nextPosition on Update as well, but it only runs on the owner, maybe something goes wrong during the ownership change

#

all players have the destination set ofc and it's synced with a variable

#

okay disabling agent altogether for everyone except the owner works

warm steeple
#

on a side note, VRCObjectSync doesn't sync rotation when rigidbody is present for some reason

minor gale
warm steeple
#

could be, not sure at all

#

but the rotation is alright on the owner

#

as soon as I deleted rigidbody others started displaying proper rotation too

#

that was back where I did updatePosition to off on both and updateRotation to off only on others

#

rn I am just doing agent.enable = false on others

fringe creek
#

Wow thank you, this is absolutely brilliant and exactly what I was picturing. Thanks for putting in all that effort, I really appreciate it!

stray bolt
#

Hey Does anyone know how to set up world generation using an object array, i'm making a backrooms world and in need of a way of randomly generating the world

tawny basin
stray bolt
tawny basin
#

I see
You should post some reference code
Also, is this networking related?

cedar flume
#

when a value is continuously synched, is there a specific amount it has to change for it to be networked?

#

I have a world with a variable that ranges 0-6.28 over 16 seconds, and while I see it as smooth, everyone sees it as changing just twice a second?

zinc hedge
#

Does anyone know what a good inbound data rate limit to shoot for is? I know outbound per player is throttled at around 11kb/s, but I'm working on a world that will have entities that generate between 64kb/s and 256kb/s of inbound traffic (between all players). I tried going to a youtube/movie world to get a sense of how much data they generate, but they surprisingly (according to debug menu #4) only generate like ~50kb/s. Not sure how they're doing that, but if it's some kind of side channel that they're streaming video through (and not saved files), then maybe my upper network activity of 256kb/s is nothing to worry about

frozen igloo
zinc hedge
#

Ok! Thank you very much. I'll just see what happens when I push towards 256kb/s. Will share results.

frozen igloo
#

Spread across how many players?

zinc hedge
#

Aiming for 32 players.

frozen igloo
#

So each player's outbound would be 8?

zinc hedge
#

Each with a capacity of ~24 object synced entities. I've observed each entity generating like 0.25kb/s, so 6kb/s per player if saturated.

#

but you're probably more accurate with it being higher. There's much more going on than just those entities

frozen igloo
#

8 KB/s is heavy but doable. It's more likely to reach that speed with a small number of large serializations. If it's a ton of small serializations don't expect 8KB/s, you'll likely have to optimize your data

verbal pebble
#

Noticed a issue where if 2 players "fight" over a objects ownership it completely breaks so that both players locally have ownership and don't sync with each other until a 3rd person requests and takes.
Also noticed this behaviour extends to the default object sync and pickups.
At first my solution was a simple "on drop" disable pickupable for 1s but alas the issue still sometimes persisted.
My dirty solution is

public override void OnPickup()
    {
     
        NetworkCalling.SendCustomNetworkEvent((IUdonEventReceiver)this, NetworkEventTarget.All, nameof(ForceOwnership),
            Networking.LocalPlayer.playerId); 
    }```

Mixed with

```csharp
[NetworkCallable] public void ForceOwnership(int id)
    {
        VRCPlayerApi forcedplayer = VRCPlayerApi.GetPlayerById(id);
        if (forcedplayer != null)
        {
            Networking.SetOwner(forcedplayer, gameObject);
        }
    }

I also kept the delay of 1s.
This feels really dirty but it works even without the delay. Anyone else ran into better solutions?
Heck even works with autoclickers with the delay disabled but uno the events pileup hence keeping the delay.

Used case is I have interactable pullable levers that sync via a single byte 0-200 and single "isheld" bool instead of floats with a custom sync frequency while held to reduce network traffic.
I know ownership is auto transferred on pickup but was still breaking on occasion when 2 people "fought" over the lever.

I Dont like dirty.

zinc hedge
#

@verbal pebble
I haven't really tested it yet, but I have a similar scenario where I have to resolve competing ownership claims. My implementation is to use OnOwnershipRequest() (https://creators.vrchat.com/worlds/udon/networking/ownership/) to reject whoever attempted to set ownership second. Original object owner would have a local bool/flag (isBeingClaimed). First non-owning player to call SetOwner will result in OnOwnershipRequest() being called, evaluated for if (!isBeingClaimed) return true and then approved by the original owner. Both Original owner and player making claim flip isBeingClaimed to True, so when the slower player attempts the claim it gets rejected by the faster player who claimed it first (or rejected by original owner if transfer not completed yet). Eventually flip isBeingClaimed back to false on both original and new owner.

verbal pebble
#

Might try it. Need to look if there is a canny for this as its a issue with the default object sync aswell.

north thistle
zinc hedge
#

I think you're right. I'll likely have to either dial down per-player entity capacity (24 -> 16) or try to do custom object sync if basic avatar overhead is that bad at higher world population. Or lower max world pop. Yeah. obviously something would have to give.

north thistle
#

if you're doing positional sync, VRC Object Sync does not have this issue; right now it is the most efficient way to sync arbitrary movement

zinc hedge
#

Oh, duh. Sorry. I just re-read your first sentence. Yeah, thankfully the bulk of the data is positional via VRC Object Sync. Fish swimming in water.

north thistle
#

If you're concerned about network usage there might be ways to make the movement deterministic, which would allow for much less network data needing to be synced.

zinc hedge
#

That's a good idea, but I think CPU is the bottleneck in my case. I'm trying to do boid-nav as cheaply as I can, but I'm still worried about the CPU cost, so I spread the ownership amongst players to distribute CPU load.

verbal pebble
north thistle
#

You can try doing some benchmarking to see if you need to optimize in that way.

zinc hedge
verbal pebble
#

I used the power of a friend and me both with a autoclicker.. works well for breaking stuff.

north thistle
#

Also I design out the possibility of competing ownership whenever possible. My systems tend to have the world master be the sole arbiter of ownership with players needing to send a request to the owner to either get something done or take ownership of something.

verbal pebble
#

Kinda hard to do with a "Physical sliding" system even more heavy when it'll come to doors and I want players to be able to "steal" the door off other players. Could just not sync I guess but thats icky.
Seems theres also a number of cannys for this issue already aswell. Dirty works for now I guess until theres a fix on vrc's end. Makes me feel better that even the default object sync has the same issue.

minor gale
verbal pebble
frozen igloo
#

Also if you're using OnOwnershipRequest at all, that changes the transfer method to something that is more susceptible to this bug

verbal pebble
#

Just using the auto request from pickup

minor gale
frozen igloo
#

It really shouldn't be common enough to need a workaround on something that is user-input driven, that's disappointing :/

minor gale
#

i found it very common in testing between two of my own clients, it seemed latency bound

verbal pebble
#

Was common enough when I let some "must touch everything" people in my test world that I had to find a fix.

frozen igloo
#

I know three players spamming transfer can definitely trigger it reliably, but between two players and especially without spamming is weird

verbal pebble
#

Yha 3rd person tends to fix the break. But just 2 and its over.

minor gale
#

in my case it was just a transfer and serialize upon receiving data, which still shouldn't really happen

verbal pebble
#

In my case even debug overlay was locally displaying the wrong owners. User A would show user A as the owner and user B would show user B as the owner. User C would be syncing off one of the 2 randomly but those 2 users were out of sync.
All fixed when user C took ownership but would still rather avoid dync situations.

minor gale
#

you might be able to turn off the automatic ownership transfer by returning early in request (locally) after the pickup, but use a network event to explicitly request ownership that the original owner has to hand off, it'd be a pain though and i'm not sure if it'd work well

i've also used proxy objects with no networking to act in that way (a pickup with a more strict ownership flow), but it's also a pain

it's a shame that the automatic ownership transfer of pickups can't be turned off in a direct way

#

when i said "seems to mitigate" it completely stopped the issue in my case, so i think it'd be worth trying

#

notably it does keep serialize requests in limbo and i only tested it with manual, the implication of the serialize flag staying on is that when you receive ownership by other means it will try to push your latest state even if you're not actively interacting with it (potentially minutes or hours later)

#

though that might not be the case with continuous (i.e. object sync)

verbal pebble
minor gale
#

i would say that your original solution probably spams transfers as it targets all players, any player can set the ownership in that way so it does look a bit "dirty" to me, but if it works

i think targeting the owner explicitly would be a little more clean in a way, but it might not result in the same behaviour

#

the fix in that case might even arise from some 3rd player handling the set owner

verbal pebble
#

Tried just owner at first and it didn't work. The other bugged user stayed bugged.

minor gale
#

ah okay

verbal pebble
#

might be able to limit it to the requester and the owner will toy tomorrow when I have other people to spam click it

frozen igloo
minor gale
# frozen igloo Conceptually, my theory of it is: - Request transfer makes the transfer take lo...

so it's something you've experienced when overriding request? in my tests at the time it seemed to eliminate the desynced ownership state entirely

people at high enough latency couldn't reproduce my case at all, while it was very consistent for me in my own region and others with low ping

without being able to see the networking code, i can only posit that the underlying issue for the desync is related to serialize arriving before the ownership update

frozen igloo
#

You likely have more recent and relevant tests on that than me, then. I haven't been able to do first-hand networking investigation stuff for most of this year

minor gale
#

i think the desync you mentioned is here, i don't think i've really tested the event prior to that time period

https://feedback.vrchat.com/bug-reports/p/onownershiprequest-disagreement-causes-ownership-desync-that-does-not-resolve

in general it's unfortunate that there isn't an explicit way to transfer ownership more authoritatively if desired (like a more explicit ownership gained via request event) as the current behaviour expects ownership to succeed if true locally, the only indication that it failed being that it eventually fires back ownership transfer to the original owner

though with network events it's a bit more possible to explicitly control the flow (barring things directly on a pickup)

#

a bubble gun with ammo is a good example of the pitfalls of ownership only logic

after picking up a bubble gun, the player begins firing and detracting from the ammo count, despite not actually having ownership (perhaps they were denied an ownership request)

their bubbles send network events if they hit other players, but there's not a good way to tell if you actually have ownership without using pre serialize in a way it's not meant for

other players could probably discard events if the player isn't the owner in their eyes, but this isn't an especially sound or good way to handle it imo

it's very hard to branch into a correct situation there without arbitrary delays (probably the most correct/simple way to handle this exact case would be to delay firing allowance for some timeframe, which is a shame)

faint rock
#

I've got a weird one here I've seen popping up occasionally - how can this possibly happen, this code: Networking.SetOwner(player, gameObject); (specifically the gameObject) throws Object reference not set to an instance of an object. Now given that gameObject means this gameobject, how could this happen?

minor gale
#

you're positive it's the gameObject and not the player?

faint rock
#

Yeah, IDE highlight from the error in console jumps specifically to the gameObject.

#

The player would make sense if they'd just left and it was triggered when exiting the world, but that's apparenty not what's throwing it.

minor gale
#

i can't say that i've seen that happen in relation to the gameobject, i would still suspect the player reference first personally

faint rock
#

I'll disregard the pointer and just harden the player checks and see if that does infact negate it then.

minor gale
#
public override void OnMasterTransferred(VRCPlayerApi newMaster)
{
    masterPlayer = newMaster;
}

this would guard against that pretty heavily i think if it's actually always the master that you want

or just using
Networking.Master

some would still suggest safety checking that with a utilities.isvalid though

faint rock
#

Oh interesting, wasn't aware that existed.

minor gale
#

i will also say that my own reference exception points to this spot (denoted by a pipe symbol after collider2)

Physics.IgnoreCollision(collider1, collider2|);

where collider1 is null and collider2 is a real reference

so i'm not sure it's pointing to the exact argument that failed, i think it's just the end of the line

#

though i wasn't able to trigger an exception while setting owner with an invalid player or a null player, so i'm not sure exactly what's going on in your situation

faint rock
#

Hmmm, could be the case. I'll keep an eye open in future for other failures and see what they do.

frozen igloo
#

It's frustrating to see things now that are still obvious, painful problems but yeesh, it has come a long way...

minor gale
#

that's why i said "if desired," it's not reasonable to accomplish in a normal or standard way if it's preferable for some use case

#

the ownership request function to someone reading through the functions may expect it allows a more authoritative stance on ownership, but it ultimately doesn't allow the type of control over it that would be necessary i feel

#

a callback akin to "on ownership request response" would be much more in line

frozen igloo
#

I think at that point, network events serve the purpose better anyway

minor gale
#

the issue is that pickups circumvent the ownership route if you plan to handle it through network events

frozen igloo
#

Ah

minor gale
#

it's possible, but it's not especially straightforward as you have to proxy the pickup

#

like i think the assumption of ownership is preferable in a lot of casual cases

linking people the ownership flow, when i tell them about the ownership request function

i eventually have to explain the fact or misunderstanding that they can't actually know that they're the owner in that flow, it's unexpected in a network context i think

so branching logic on that is odd (which is what i tried to highlight in the bubble gun example where someone was effectively dealing damage to people without owning the bubble gun; but prior to receiving notification that they lost ownership)

#

i think the most intuitive thing to most people, or at least me, is that ownership transfer would not fire until ownership was actually transferred if overriding ownership request

hoary frost
#

Just to make sure im not goofing it.
Ive got a hashset of a custom class with a bunch of data in it related to a player (ID, name, ect...)
And i want to network a bool thats inside this class (but dont need to network everything within that entry, only the bool)
How should I do this?

#

Im thinking just a separate udonsynced int[]

#

But itll be annoying to make sure each entry in the hashset is at the same position as the udonsynced int[]

#

otherwise im not sure how im supposed to cross reference these two arrays

#

In code format if its easier to understand:

internal class PlayerEntry
{
    internal GameObject entry;
    internal UIButtonSender button;
    internal VRCPlayerApi playerAPI;
    internal int ID;
    internal string displayname;
    internal bool isToggled;
}

public class PlayerList : UdonSharpBehaviour
{
    HashSet<PlayerEntry> playerList;

    void Toggle(int i)
    {
        playerList[i].isToggled = !playerList[i].isToggled //This needs to be synced for everyone
    }
}
#

hmmm actually, maybe i could send a customnetworkedevent with the ID of the entry to identify it
But then syncing it to late joiners could be a hassle

#

might be hard to do without a ton of networked events directed at a new joiners so that they recieve the info

minor gale
hoary frost
minor gale
#

if i'm understanding it correctly, i think the easiest thing you could do is an int[] and a bool[] array, where you work on them in tandem

[UdonSynced] private int[] elementsLookup;
[UdonSynced] private bool[] elementsState;

private void FillSyncStuff()
{
  elementsLookup = new int[playerList.Count];
  elementsState = new bool[playerList.Count];

  // Iterate here
  int i = 0;
  foreach (kvp something something)
  {
    int key = kvp.Key;

    elementsLookup[i] = key;
    elementsState[i] = playerList[key].isToggled;
    i++;
  }

  RequestSerialization();
}

i don't know the syntax you need for iterating over the hashset to fill it, but i think something like this should work?

#

making your own byte[] serialize and deserialize would also work well, where each element is preceded by its identifying int

#

and if your hash set is derived locally (from join/leave events and on start or something?) i think you'd want to safety check that the element was actually there when you deserialize and look into the hashset, or just use tryget, not sure how you're generating entries though

hoary frost
hoary frost
#

i thought it was only int[]

minor gale
#

you can udonsync pretty much all of the primitive array types

hoary frost
#

oh awesome

minor gale
hoary frost
#

im instanciating them

hoary frost
#

even if we dont find better, this feels robust either way, thank you occala :)

minor gale
#

so you'd only send the identifiers of those actively toggled; toggling them off if found locally, but not found in the synced array?

minor gale
#

that could work

hoary frost
#

better than two synced arrays right?

minor gale
#

better is subjective, i think the two array way is the most simple and readable to start, but you can save on data if you pack it differently, i'd probably use a byte array personally

your idea for only sending active ids would work though if you need to save on data

hoary frost
#

i suppose i got lucky having IDs here.
If i wanted to sync more properties, like lets say a string, per entry, that would be harder with a hashset and i might have to resort to a more ordered data type

hoary frost
minor gale
#

having more properties you'd want to move to a byte array i think

#

it being ordered doesn't necessarily matter

hoary frost
#

hmm oh yeah, because the lookup of which value is being modified is just based off of where it is in the original byte array

minor gale
#

no, you'd preface each entry in the byte array with the identifier

#

more like a header

#

followed by the string length then string value, which means the read length for that element is known and you can parse as many entries as the length of the byte array

#

it's a bit more advanced and you should look into packing in relation to serialization/deserialization if that's actually something you need to do

hoary frost
minor gale
# hoary frost hmm how so? can you show me a brief mockup?
// Writer side
int writeIndex = 0;
byte[] packedData = new byte[playerList.Count * 4]; // Each identifier is 4 bytes

foreach kvp -
  int identifier = kvp.key;

  byte[] identifierBytes = System.BitConverter.GetBytes(identifier);
  System.Buffer.BlockCopy(identifierBytes, 0, packedData, writeIndex, 4);
  writeIndex += 4;
// Reader side, being passed the packed data (we'll call the bytes packedData again)
int readIndex = 0;
while (readIndex < packedData.Length)
  int identifier = System.BitConverter.ToInt32(packedData, readIndex);
  readIndex += 4;
#

this does nothing other than pack each identifier sequentially, but it's an example of how you might write the header, on both sides you'd sequentially read or write more data within the same entry if you had properties you needed to pack

hoary frost
#

so basically youre turning each int into a more compact and performant version via bytes?

#

thought just the simple act of having a byte array would already be a performance gain

minor gale
#

it's more like a rigid structure, you can think of it like a data entry

  • int
  • int
  • int
#

if you were to pack a string after each entry, you'd know about a string related to each identifier when you pack or unpack

- int
  - string
- int
  - string
- int
  - string
hoary frost
#

mhmm sensical

hoary frost
minor gale
#

i was operating on it like a dictionary, you can just imagine it's iterating through a list or an array if that's easier

#

they also do a pre pass kinda thing in the doc example, where they don't know the length in advance because strings are variable, so they get the bytes first and calculate the size before assembling their final byte array they utilize

#

it might not make as much sense because i only included one data point per entry

hoary frost
#

hmmm yeah i dont really understand this, i think ill review it after i took a look at the docs you sent

minor gale
#

i might be bad at explaining it, it is a little different if you're not familiar maybe; basically with a consistent write and read format, someone can deserialize the bytes to the resulting properties of the entry

like if you were packing some properties in series, you could use 3 arrays like this, but it becomes kind of difficult to interact with if you add more types

[UdonSynced] private int[] someIntArray;
[UdonSynced] private float[] someFloatArray;
[UdonSynced] private bool[] someBoolArray;

with bytes you can just define how to read and write each entry, in this case you'd read an int, then a float, then a bool and if you're not at the end of the byte array after reading those, you know there's another entry; so you again read another int, float and bool

[UdonSynced] private byte[] someBytes;
hoary frost
#

but youre essentially packing each data type into a single byte entry

#

efficient

minor gale
#

in udon land it's probably more expensive to both pack and unpack in that manner, but it's more versatile and good to know how to do compared to rigid explicitly typed arrays

hoary frost
#

For sure, ill do some reading on it when ive got the chance

#

Prob useful outside of udon

warm steeple
#

I was wondering, is it possible that nobody has ownership of an object? assuming both sides always agree on receiving the ownership at all times.

#

in my testing sometimes things my udon behavior is supposed to do on owner just get to a state where they are never done

#

so I assume the IsOwner() check fails for everyone somehow

#

there is also a possibility the owner will try to set the ownership to two different players at the same time, that is also not good

warm steeple
#

so what I should do is make a failsafe on master where if nobody is the owner then master gets the ownership

finite sierra
#

eh. no cause ownership always transfers it self if the one that had it before dc/leaves etc. you cannot rely or should rely on master ever

warm steeple
#

I am not relying on master

#

I just want to prevent a situation where no one owns the object

#

since that breaks my game world very heavily

#

and a new instance has to be made

#

however I am not 100% sure it's the issue of not having ownership, exploring the causes rn

hoary frost
warm steeple
#

that's what I believe too

finite sierra
warm steeple
#

that's good to know

#

any idea what's happening when I try to test the world? this started happening after I regenned my network IDs but they look alright. I have the world uploaded to VRC as well, I think it gets confused somehow

minor gale
#

there's a period of time during ownership handoff where someone will be unaware of their ownership status, especially after a leaver or if the ownership is passed by a player other than themselves, so it would be possible for no one in the instance to process something you do in udon logic on the owner's end (in their local view they would not yet be aware of their ownership), despite there always being an underlying owner in the server's eyes

whether that's what you were experiencing previously i can't say, that case should be pretty rare (though it should happen if you were doing owner-only logic in update for example)

(this is unrelated to the issue you posted about just now, it's more in response to ownership)

warm steeple
#

though it should happen if you were doing owner-only logic in update for example
yeah I am doing that exactly

#

AI state machine

#

it chases the player, each time a new player gets chased the ownership is passed to that player for them to do all the logic

#

it worked fine when all of it was done without changing ownership from master but due to severe delay between the master and other players it wasn't acceptable

#

as I also do raycasts when the player is near and decide whether the player should be damaged

minor gale
#

if the current owner is passing ownership there would be some timeframe where both the original owner would believe they are no longer the owner and the new owner would not be aware until they received the ownership update

whether that breaks your logic i can't say (ideally it wouldn't break your logic though)

warm steeple
#

it's alright if it lags a bit, sadly even after the ownership is transferred everything gets to a broken state

#

so there's an issue somewhere in my logic

#

sucks that it's hard to reproduce

#

I am adding a lot of debug logs to get a better picture

warm steeple
#

also one thing I figured out recently is that if a gameObject is disabled the udon behavior will stop serializing

#

I was doing a simple synced on and off script and was putting it on the same object that was being toggled, that's a big no-no

#

it works with events but not variables

finite sierra
#

@warm steeple you should never disable networked objects in udon. As it will cause unwanted effects and potentially. Breaking everything

warm steeple
#

yeah

#

figured as much by now

#

sadly I have a thing I can interact with that also needs to be disabled

#

and there are 71 of them

#

so for now I just decided to move it really far away instead

#

a workaround but good enough for me lol

harsh pebble
minor gale
warm steeple
#

did some testing and finally nothing is breaking so far, I did notice the AI agent get duper fast for some time on rare occasions, I think that was VRCObjectSync catching up after ownership transfer but I won't bother fixing that, it's funny actually

finite sierra
warm steeple
dreamy plaza
#

is it just me or is there no official documentation on how to use the manual sync mode?

minor gale
# dreamy plaza is it just me or is there no official documentation on how to use the manual syn...

this describes the sort of event-driven flow you'd use for the enacting player (OpenDoor) and remote players or late joins (OnDeserialization)
https://creators.vrchat.com/worlds/udon/networking/late-joiners/#1-use-synced-variables-instead-of-events-for-state

i'd note that the doc here doesn't showcase how the setting player should derive the door state from the variable change though (as they do not fire OnDeserialization); nor does it show setting ownership

warm steeple
#

I do it in OnPostSerialization

#

that way it won't desync I believe

elder girder
#

UdonBehaviours on GameObject 'Glow Stick' are using manual sync
while VRCObjectSync is on the GameObject, this can cause sync to work unexpectedly.

grrr 😡

twin portal
#

That's to be expected, you can't mix sync types on the same object

spiral basalt
silver hill
#

I'm having issues networking NPC locomotion animations, for the host, NPC's walk around and do their walking animation in sync with their current velocity that their nav mesh agent is, but for joined players they just stay in their standing idle animation while moving, I tried to fix it using different methods of calculating speed such as measuring distance between ticks but it's being stubborn and just not utilising the animator walk cycle floats for joined players

vapid pagoda
silver hill
#

cont and object sync yes

silver hill
vapid pagoda
silver hill
#

interesting, I'll see what I can do

north thistle
#

my own system, I really wanted nav agents to be able to have inter-avoidance which created a chaotic enough system that only synching the destination would produce too much desync

so instead I had the walk animation triggered by the synched npc state

with that said, on the remote player's end, if the npc has a rigibody, you might be able to pull velocity from that

minor gale
#

i don't think kinematic rigidbodies or those moving under object sync will provide useful velocity, but calculating it yourself remotely is easy enough

tulip sphinx
#

ye they def wont cause a rigidbody is only enabled on the owner afaik🤷‍♂️

north thistle
#

at least I'm going to presume it will not

crimson shadow
#

im struggling to understand how TeleportPlayersToGame() is not being called twice
when i have two players in the scene, the remote player [1] and the local player [2], i expect it to be called twice, but apparently not..
am i doing something wrong?

    private void Update()
    {
        // ... some code ...

        if (GetGameState() == GameState.Ongoing && !gameStarted)
        {
            // lock in decision for whoever is playing
            playerManager.SetPlayersPlayingIDs(lobbyManager.GetPlayersInsideIDs());

            // TODO: Set player teams and teleport
            playerManager.AssignPlayerTeams();
            SendCustomNetworkEvent(NetworkEventTarget.All, "TeleportPlayersToGame"); // only calls it once, yet there's two players. what????

            gameStarted = true;
        }
    }

    // ... some code ...

    public void TeleportPlayersToGame()
    {
        Debug.Log("Test"); // only gets printed once for some reason i cannot understand
        VRCPlayerApi localPlayer = Networking.LocalPlayer;

        if (!playerManager.IsPlayerPlaying(localPlayer))
        {
            Debug.Log($"Player {localPlayer.displayName} isn't playing the game and will be left behind...");
            return;
        }

        PlayerTeam team = playerManager.GetTeam(localPlayer);
        Debug.Log($"Player {localPlayer.displayName} is on team {team}");

        // ... teleportation code ...
    }
#

only the local player's being teleported, should be teleporting the remote player as well

twin portal
#

it gets called once, for each player

#

each player will locally run that function once, so you'll only see the output log once if you're only viewing the logs from one client

crimson shadow
#

so how can i log it from other clients

twin portal
#

you'd have to send another event to everyone saying that they've ran it

#

though, is doing that necessary?

#

you could have a custom network event with 1 parameter that takes a string, that then outputs that string to the console

crimson shadow
# twin portal it gets called once, for each player

if thats the case, then

public void TeleportPlayersToGame()
    {
        VRCPlayerApi localPlayer = Networking.LocalPlayer;

        PlayerTeam team = PlayerTeam.Wanderer; // changed to Wanderer as a test
        Debug.Log($"Player {localPlayer.displayName} is on team {team}");

        switch (team)
        {
            case PlayerTeam.Wanderer:
                {
                    localPlayer.TeleportTo(wandererSpawn.position, wandererSpawn.rotation);
                    break;
                }
            case PlayerTeam.Entity:
                {
                    localPlayer.TeleportTo(entitySpawn.position, entitySpawn.rotation);
                    break;
                }
            default:
                {
                    Debug.Log($"Player {localPlayer.displayName} is on team {team}, which isn't valid for this game.");
                    break;
                }
        }
    }

how come doing just this, and calling it via SendCustomNetworkEvent(NetworkEventTarget.All, "TeleportPlayersToGame"); still only teleports the local player?

#

im so confusedd

twin portal
#

the local player as in, the player sending the event?

#

you can only teleport the local player anyway

crimson shadow
#

the local player as in "non-remote" players

#

if you know what i mean

#

you did say it should run on each client locally, so the remote player should be teleported as well because of localPlayer.TeleportTo(...)

twin portal
#

that's right

crimson shadow
#

...so why is that not happening?

twin portal
#

as the event should send to every player, who would then teleport themselves

#

what sync type is your Udon Behaviour set to?

crimson shadow
#

manual

#

hmmmm

#

maybe thats why

twin portal
#

it should still send if the type is manual, as long as it isn't None

#

so you aren't seeing any logs on remote clients when the sender calls the event to teleport?

crimson shadow
#

no

#

let me rec a video

crimson shadow
twin portal
#

oh. lol. it's not going to work in ClientSim

#

remote players in ClientSim aren't real players, they won't execute network events that have been sent to them

crimson shadow
#

oh?

twin portal
#

you'll need to test with 2 real clients

crimson shadow
#

so i have to actually open up vrchat

twin portal
#

you can build & test with 2 clients

crimson shadow
#

oh my gofdh

#

it works in vrc

warm steeple
#

it's cuz of default event send rate of 5

#

ended up using synced vars, made much more sense for what I was doing anyway

north thistle
#

If you're doing a lot of network testing, can you get temp blacklisted by VRC? It's happened before and now it's happened again, where after doing a lot of testing I've suddenly been hit with "cannot authenticate with the realtime server" errors.

warm steeple
#

yes that happened to me once

#

I was temp banned

#

and I bet it's a measure out of vrc control since they use photon service

tawny basin
#

If there's a way that people collect analytics, what's the common approach?
For example, if I have 3 different options for starting a gamemode and I want to see which option people choose the most globally would that be feasible and possible?
I figure it might be an untrusted URLs thing which isn't optimal, but it works

tulip sphinx
#

yes afaik untrusted url is the only way to send some collectible data outside

warm steeple
#

I'd make it opt in even if we were allowed to use untrusted urls at all times, better for privacy

#

you could make it so that if the player agrees to the collection but doesn't have untrusted urls enabled you catch the error and don't let people in unless they opt out or enable untrusted urls

tulip sphinx
#

its anonymous by design, people can not enable untrusted urls if they want. trying to link any data to an actual user is punishable, no harm in a simple counter that doesnt even reflect total amount but rather ratio

safe path
#

Does anyone have a good example for syncing an integer w u#? I’ve been struggling very hard on this.

cedar crescent
# safe path Does anyone have a good example for syncing an integer w u#? I’ve been strugglin...

This is with manual sync.

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class IntSetting : UdonSharpBehaviour
{
    [SerializeField, UdonSynced] private int valueSync;

    public void SetValue(int newValue)
    {
        Networking.SetOwner(Networking.LocalPlayer, gameObject);
        valueSync = newValue;
        RequestSerialization();
        ApplyValue();
    }

    public override void OnDeserialization()
    {
        ApplyValue();
    }

    private void ApplyValue()
    {
        // React to the value changing here!
        // Like if the number were a score, you'd update the scoreboard here.
    }
}
safe path
cedar crescent
safe path
#

Would labeling my recursive functions as recursive fix it?

cedar crescent
safe path
#

Also, should I be setting owner every time I sync something. In the past, I’ve just checked if they’ve been the owner of a specific object. And since I don’t change object ownership only one player ever runs the code.

cedar crescent
safe path
#

If someone goes afk or their tab goes to sleep will networking get screwed up?

tulip sphinx
#

thats not a thing on pc. on quest it is

#

they once again rewrote the docs so theres nothing on the subject now

twin portal
safe path
#

I may just run import stuff on the owner instead of letting the clients run it. I think it’ll be cleaner.

tulip sphinx
#

the what

north thistle
# tulip sphinx thats not a thing on pc. on quest it is

I have never heard this occurring on Quest either; has anyone with a Quest actually tested this?

I strongly suspect, but haven't tested, that this is a thing oh the mobile version of VRC, however. Of note in the docs: "The master player may also change if they're on Android and kept VRChat in the background for too long." - https://creators.vrchat.com/worlds/udon/networking/ownership/#the-instance-master

wintry bear
safe path
wintry bear
safe path
twin portal
#

if you're using delayed seconds, then it isn't recursive and doesn't require the attribute

wintry bear
# safe path Send custom event delayed seconds. I call it recursively but I haven’t tagged i...

Personally I'd use a fixed update loop that decrements the timer value for something that's recurring like that. For more accuracy you could also pass the local time the owner sent it at, compare it to local time when received, and do a little math to get it lined up with the master. Does assume that both machine's clocks are relatively up to date, though there's other ways to account for even that. All depends on the precision you need.

safe path
#

I think I can just have the owner run the timer and send a network event when needed.

wintry bear
# safe path I think I can just have the owner run the timer and send a network event when ne...

Keep in mind that network events don't late sync state, and if an event gets missed it just gets dropped.

I don't know all the details of what you're making, but my recent Cube world does something similar. I sync a random seed value and the start time in milliseconds once at the start of a game. From those values players run their own generation and timer logic locally that determines the structure of the cube, what round it is, when rounds change over, etc. one-off events are triggered by network requests, such as requesting networked item spawns from the master, opening hatches, etc. But those states all resolve functionally well with late joiners, and don't break the core game structure or loop if they're missed.

safe path
cold laurel
#

If a Quest user takes off their headset but keeps it powered on then the VRChat application will be suspended. The player is still in the instance and may still be the network owner of objects or be the current instance master.
However, they'll be unresponsive. This can break systems such as Cyan's Player Object Pool that rely on the instance master to behave as a server.

cold laurel
north thistle
safe path
cold laurel
# safe path Do you have any good tutorials or guides, most of networking is voodoo black wiz...

VRChat's Udon networking paradigm is inherently very "peer to peer like". For situations that are well suited to that structure you'll have a much easier time if you lean into that.

If you can define your entire game state as UdonSynced variables then that would be optimal as you won't need separate logic to handle late joiners.

That's of course only if your game state isn't too big™

safe path