#udon-networking
1 messages · Page 10 of 1
Forgive me, I'm a bit of a noob when it comes to Udon networking, so I have no idea how I'd set up an event buffer.
I'm using CyanPlayerObjectPool, to give you an idea of what I'm working with.
does anyone know how to make a whitelist only door toggle? for like clubs with age verify.
Check the username of the local player triggering it, and if not on the list, don't run the code for opening it.
i have barely to no udon experience 😭 but ill try to figure it out
I still need help with this... How would I implement this? Is there a package that would enable this without having to reinvent the wheel?
Personally im using networkeventcaller to send information for when a player deals damage to a target, maybe that could help you
Oh, thank you! That's exactly the kind of package I've been looking for to do this, but never came up in my search results.
somewhat of a silly question / sanity check: what exactly happens when RequestSerialization() is called from someone who is not the owner of the object it's called on? my initial assumption was that it would either do nothing, or ask the owner of that object for the most up-to-date values of its variables, but i'd like to confirm for sure what it's doing
like, say for example one player is the owner, and one player is not (ofc), and the object is synced manually. both players run a method that increments a synced integer by 1, and calls RequestSerialization() (first the owner, then the non-owner after the network settles). assuming the integer starts at 0, my initial assumption was that the integer would only increment by 1 for everyone (thus now equaling 1) since the owner is the only one who actually sends out the new data, but would that be incorrect? or would that second player then increment it to 2 and sync that new value? or?
its doing nothing
Also the value doesn't "increment by 1". The value is set and the value is "copied".
semantics, but yes i know
and okay thank you !
Not a problem I thought incrementing might seem like it was doing something different. 🙂
Next time, if you also explained where and how it failed, that will help tremendously.
Does anyone know if " SendCustomEvent" broken?
That is extremely unlikely.
If you're having issues with a script you should provide more context so we can help you.
I have a script here wanna use a collider to fire an animation on a mesh
The same script worked on the last version but not the current one
The animator script is working fine but it doesn't fire an event to the other one like it should on the previous version of the world
Animation works just not the event sending to the script like the old one did
I m puzzled
There are a few things that could be wrong here.
- The references aren't set correctly the inspector
- The eventName isn't correct
- You forgot to put a trigger collider on the same object as this script
Also, this is unrelated to the current issue, but it sending the exact same event for enter and exit is just begging for this to end up desynced.
The reference is on the gameobject itself, both animator and scripts.
Event name was copied from the script.
Collider was a box collider works fine with local trigger not through an event
Hmmm, is there a better way to do this. it's a walk through curtain. But I m still fresh to Udon and scripting
You should have an event for entering the trigger and a separate one for exiting.
This way you can guarantee that it will be open when you're in the trigger

These enter and exit events are not very reliable, and oftentimes just don't fire
So you can end up with it closing when someone enters
With your current system
Which event name is it specifically


that is the problem... hmm idk Q_Q
Here's a pretty comprehensive tutorial on this type of thing
https://youtu.be/fjHP80CQzZ4?si=ghtzUM8JU723HhLJ
The main issue could also be that you have the gameObject with the collider on the wrong layer

Like, there are so many things that could cause it
I have the collider on the mesh itself too
The curtain gameobject itself~
I... I m confused, it worked again. All I did is just created a new Udon script, pasted it to the new file
Sometimes udonsharp scripts just stop working without any reason. Removing and re-adding the script magically fix things.
right
Super random technical question, but, does anyone know if it's possible to override where a non-local player is displayed on a local client? As in, setting a position of a player, but not actually moving them there, it's just to display on the local client.
Related to above post but also there's more details involved when talking about it;
Hey @cold laurel I've seen your worlds and your github code and you seem to be very close to what I'm trying to do, and extremely knowledgeable on it, is there any chance I could talk to you about a project I'm working on, and get your advice on what approach I should take? I can dm you or talk here, totally your call. I hope to get some advice from someone as much of an expert as you!
I would prefer having the discussion here so it can benefit others too.
Totally! So, I'm making a world with a driveable bus that's meant to simply be a social driving world, and I want to make it so players can walk around in the bus like normal and have no problems even while the bus is in motion. I have two things I need to fix, and the solution to one seems to slightly get in the way of another.
- I want players to be able to move around the inside of the bus without any problems, and still move with the bus as it moves.
- I need to be able to display players in positions they are not, so that I avoid the problem of everyone lagging behind the bus because of network latency if they are standing up. I want to avoid the problem of locally you're the only one standing up on the bus and everyone else is trailing behind.
I'm aware of using stations with immobile only set on remote clients, that's a possible solution for #2, but #1 my original solution uses teleporting to match the moving platform's motion and unfortunately teleporting a player un-seats them from any station they are in, regardless if they are allowed to be mobile.
I could use a station to solve both problems; ie, have a fake player controller inside the bus that's parented to the bus, and then network it's position however I please, and therefore allows them to have no problems moving around, but as of right now, players using a fake player controller (via a station they are forced into) makes their walking animations not play, and desktop has some neck turning problems, I'm wondering if there's any solutions that don't disable walking animations?
@tired bone https://github.com/Superbstingray/UdonPlayerPlatformHook this system uses stations, and then falls back to a teleport if stations are not involved, so it might be your solution for #1
I’m not sure what it looks like to watch a remote player interact with this script, though, I’ve never actually seen it in play
unfortunately that's my current solution to #1, and it uses teleporting only, not stations, and it does produce lag-behind distance because it's still networking your position like normal which will lag relative to the car position
The only current solution here is to split your world into two reference frames
One for physics, and another for visuals
Here is an example of me playing around with this concept last month
I wanted to be able to walk around inside my spaceship while people were piloting it
Your use-case is a bit simpler as I assume you only have one bus to worry about
So you won't need to desync player locations via stations
People will be able to walk around outside the bus, ideally
Yeah, I think I know what you mean though, you mean that you have the actual physics going on, and then all the visual models are separate and relative to what's your current visual position, and you can offset players by using the immobile-only-on-remote trick, which keeps animations?
Yep
Basically, when you enter the bus you put the visual part of the bus at 0,0,0 with no rotation
then display the rest of the world in the bus physics object's local space
when you exit the bus, just undo all of this
Oof... I know that's possible but dang that's a big programming task, haha
a few hours
Maybe if you have existing expertise, haha, but I think I know what you mean. Problem is that it means you can't have any static objects and the like I'm assuming?
correct, absolutely no static batching
and the overhead of moving the whole world every frame
And it makes everything else in the world harder to do. Like, if you have other rigidbodies or particle effects that rely on collision
Oof...
it might be better to just have walk animations not work when only inside the bus, and then maybe wait for vrchat to add the ability to fix some other aspect that makes all this even necessary to work around. I totally appreciate the way you think about it though!
because the performance overhead is kindof a dealbreaker sadly
if it was performant on larger more complex worlds I'd be totally down to try it, but ideally I'd like to have significant detail in my world
This sounds like a good compromise
Hopefully we get the ability to parent the player to objects without stations sometime
yeah that would be good, or to be able to set animation parameters, because all we'd have to do is tell the player's animator that they're not in a station
we CAN set custom animation controllers for players on stations, but obviously one walk cycle isn't going to work for all avatars, so it wouldn't quite work the way one would expect.
thank you for your insight, and hopefully vrchat continues to improve giving us access to more great worlds like your space ship world!
@cold laurel are you able to manually trigger VRChat animations from a world interaction in the same way NUSS writes into custom animations to save content?
Afaik NUSS just sets the velocity of the player
The thing is, that requires the player be in a mobile station or not a station at all
🤔 I thought it literally called something that triggered an animation on the avatar if the name exists
I’m gonna DM him about this, if I’m completely wrong, I have been wrong this whole time and not known lmao
Weirdly enough some stations can trigger the avatar to think it's not in a station for one frame when you open up the larger static menus, which is why gogoloco can make a player stand up weirdly on some stations sometimes, but I think it's only a glitch on vrchat's side and we can't set that up to reproduce ourselves, sadly.
Little question.
I couldnt properly find any documentation con confirm what i'm looking for atm, but when working with UdonSharp, when using a String Array, does UdonSynced work on that too? I found a list of variables it would work on, but it never said anything if it worked on arrays.
Afaik you can sync an array version of any of the types mentioned in the supported types list.
However, you must ensure that every client has the array initialized (not null) and that every element inside the array is also initialized.
For a string array to sync you must ensure that all the elements in the array are not null.
how will networking work when after changing variables and requesting serialization i instantly change owner to someone else
That's probably a race condition. Depends which arrives first, the variables or the ownership transfer. If the transfer arrives first, the variables would be ignored
so im trying to make this only work for the player that enters the area like local but the problem im having is it does it for everyone
so if someone else leaves the area it also updates for everyone
i want it to just be one person that this effects
i turned of synchronization and turned it to none
and i thought that would work but it doesnt
how do i do this
GameObjects are not synced at all. They are local.
I have never touched graph. Is there like some networked event to turn the gameobject on/off?
i guess
i thought it was all local without like asking it too but i guess what i was doing was automaticly?
but i got this and i hevent test it yet but gimme a min
how do i run to people at th same time in the editor agian?
Yeah, you don't want to use SendCustomNetworkEvent unless your wanting to send that to everybody.
I'm not to sure if this is what you created after I sent my message or before.
so how do i make it just do it for one person?
i feel like i over complcated it
but i hevent tested it yet so one min
It should be this.
it updates for everyone
You want the mirror to only react to the local player, right? OnPlayerTriggerEnter and OnPlayerTriggerExit events occur when any player enters or exits the trigger. So you can check which player they are before toggling the mirror
That is what I was about to say next.
But I assumed it was local by default but would make a lot of sense.
i love you
is it like checking if its that local player that did it and if it is it does true?
yeah!
SendCustomEvent is not networked, SendCustomNetworkedEvent is networked
if player spams serializations in some object how to properly assign owner as master so player wont override anything with serialization spam
Hello, I have asked kapa ai about Udon, I wanna attached a gameobject to players like those game world did.
however the script it provided is not complete/broken. Does anyone else here know the fixes?
[SerializeField]
private GameObject objectToAttachPrefab; // The prefab for the object to attach
private Dictionary<VRCPlayerApi, GameObject> playerObjects = new Dictionary<VRCPlayerApi, GameObject>();
private void Start()
{
int playerCount = VRCPlayerApi.GetPlayerCount();
for (int i = 0; i < playerCount; i++)
{
VRCPlayerApi player = VRCPlayerApi.GetPlayerById(i);
playerObjects[player] = CreateGameObjectForPlayer(player);
}
}
private void Update()
{
foreach (var playerObject in playerObjects)
{
if (playerObject.Key != null && playerObject.Value != null)
{
VRCPlayerApi.TrackingData headData = playerObject.Key.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
playerObject.Value.transform.position = headData.position + Vector3.up; // Position the GameObject above the player's head
}
}
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
playerObjects[player] = CreateGameObjectForPlayer(player);
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
Destroy(playerObjects[player]);
playerObjects.Remove(player);
}
private GameObject CreateGameObjectForPlayer(VRCPlayerApi player)
{
GameObject gameObject = VRCInstantiate(objectToAttachPrefab);
gameObject.transform.SetParent(transform);
gameObject.name = $"{player.displayName}'s GameObject";
return gameObject;
}
}
You might not even need a player object pool for this

id array
does ondeserialization get called automatically when you join?
any idea why this isnt being synced properly in OnDeserialization?
this is the length of the arrays from the late joiner client
and this is from the first client that set the values, which should be synced but aren't for some reason
nevermind I'm stupid, I forgot you need to do array = array.Add() with UdonArrayExtensions instead of just being able to do array.Add()
we can serialize some arrays? Oooh interesting, guessing not arrays of gameobjects?
For a late joiner, do they automatically request serialization data for a behavior if the behavior uses manual sync, or do I need to override onplayerjoined and request it?
auto
that's good to know actually
#udon-networking message while i have the link in clipboard
This is amazing and I’m happy that I just popped in here and noticed it
If an owner of an object changes a synced variable, but does not request serialization, does that new value still get serialized when a new player joins?
Trying to handle the case of the object owner leaving, but still needing to set some synced variables
I'd bet a lot of money on "no". It's a simple process that transfers data. The data needs to move from the client that modifies it to the server and from there it is distributed to all other players. It won't move the data values "up" until they are all assigned new values and they way it knows that has been done is the client calls RequestSerialization.
What I mean is when a new player joins, they get serialized data. But I wonder if that's cached or not.
If it's not then I can just set it locally for all players, then the new player will get that data serialized
They get data but whatever "everyone" already has.
The data has to be sync'd that's the key.
It always works the same. Take ownership of the object, set the sync'd values, call RequestSerialization.
Ah nuts
I'm trying to handle releasing a 'station' (not a VRC station) when a player leaves, but I'm unsure of the way to go
I imagine if a player ungracefully disconnects, OnPlayerLeave is called while they still own it, and I'd imagine at that point that client cannot call RequestSerialization
I was thinking of doing in OnPlayerLeft some kind of ownership transfer to master, but then what happens if the master leaves
What I do (someone was holding/owning one of my objects when they left) is have the master player check if the player that left owns anything and cleans up the object.
When the master leaves all objects transfer to the new master.
There is always a master until the last player leaves.
For that I wonder if it happens before or after the Left callback
If it happens before then I can just transfer ownership to the master in OnPlayerLeft
public override void OnPlayerLeft(VRCPlayerApi player)
{
if(HasPlayer() && Networking.LocalPlayer.isMaster)
{
BootPlayer();
}
}
public void BootPlayer()
{
havePlayer = false; // synced
interactedPlayerId = 0; // synced
HandlePlayerLeave(); // sets up UI stuff, gets called on Deserialize when havePlayer = false
RequestSerialization();
}
I think you can count on all the "internal stuff" happens before we are notified. So transfers have occurred and then when the system is stable you are told the player left.
In that case this should work then 🤞
Does anyone happen to know if vrcobjectsync transfers impulse values when a synced object collides? Would it trigger OnCollision for remote players? Making a script that lights up an object based on its collision impulse magnitude
@harsh pebble no, only the owner runs physics on the object, for everyone else those components are disabled and they just get transform
Makes sense. Ill have to do a custom sync for the collision impule or something then. Thanks!
Did phasedragon ever post a completed version of the networking presentation other than the linked one here? I wanna come back to it
dont think so
hey, does anyone know if "OnPlayerJoined(), that joining player obtains ownership of every object in the instance" is a bug that vrc is working on fixing atm ? it's a bit game-breaking .-. unless that behavior is intentional for some reason ? in which case, is there some easy way I can circumvent it for objects that rely on keeping the owner I assign to them, without also preventing ownership transfer OnPlayerLeft() ?
whaaat, I've never heard of that happening. Can you describe it in more detail?
I feel like I would've heard of that happening by now. When did it start? Is it only under certain conditions? Is it permanent, or only for a brief moment?
i'm not 100% sure exactly when it started, but the first time my community reported it to me was 3/24 (they just reported things "unlocking randomly" but after looking at the logs, my best guess is that's what's happening)
I only have hard proof of the game setting ME (the creator of the world) as owner once I join, but iirc i've seen a log where it happened to someone else in the community as well
i've attached a screenshot of what happens when I join the world. a script reports that I'm the owner of its corresponding object, despite the fact that i've JUST joined, and below you can see the username of the previous owner (that should still have ownership)
i'm not sure exactly how "permanent" it is, but i do know the ownership transfer remains in effect for at least a couple minutes, so i'm inclined to believe it is permanent
it could always just be my fault somehow, in which case I apologize if that ends up being the case, but i've been stuck on this issue for weeks and i've read the codebase through countless times, and i can't imagine any part of my code that would cause this
i just read through another community member's log that actually didn't have this issue. could it be that it only sets the world creator as owner upon joining ? also, could it possibly have something to do with that recently-implemented "dynamic master switching" ? or does that not touch object ownership
The "dynamic master switching" you speak of is when the current master times out or leaves, all Objects not given explicit ownership through Networking.SetOwner will have their owner set to a player VRChat deems to have the best system and networking capabilities. This is in pretty stark contrast to the traditional FIFO system that was in place before. The timeline you describe lines up pretty decently, but the previous master has to "time out" or leave for it to trigger. For "timing out" for quest users, it's when they have their headset taken off for some amount of time I cannot think of atm.
right, i believe that time was around 4 minutes. but that doesn't seem to be related to my issue? i become owner as SOON as i join ANY instance of my world.
unless you mean "timeline" as in "the bug began around the time the master switching change was implemented" ?
but in any case, i don't expect the new system to be the issue per se, but i was rather implying/suggesting that perhaps while modifying the code to implement that change, maybe some ownership-handling code was messed up accidentally in the process, or that there was some edge case that wasn't accounted for, etc.
are you absolutely certain it's not some code in your own systems that does this? Have you reviewed all the places where you do networking.setowner?
i was looking through the directly-related scripts earlier and couldn't find anything, but i think when I'm at my PC tomorrow I'll take a thorough look through every script (even completely unrelated ones) to see if there are any rogue SetOwner calls that could be causing this. i'll let you know whether or not i find anything !
I'm not too sure if this would be considered an udon question but I'm guessing it does have to do with networking.
I was thinking about how a video player works and how it connects to YouTube to be used in vrchats system. I'm not talking about preloaded videos directly from YouTube. I am talking about having direct access to YouTube and being able to search up videos while in the world on the video player and all the videos you want are added into a playlist/watching next list. There's no tutorials on how this works though. How would one make set something up like that?
Searching YouTube is a very advanced topic. It involves far more than just VRC and Udon. It isn't directly related to video players either (a video player simply plays the URL it was presented). I am not aware of any articles or tutorials on the subject.
Some stuff is local, some is global. So stuff is usually happening the person that casts an event or to all. But what if I want to happen to everyone BUT the caster. If the caster uses a spell on anyone but themself or even catch a specific person that is not the caster of an event. Anyone knows how to achieve that?
so i'm not sure if this is exactly what's happening, but I found something in my code that could theoretically cause this issue in some rare cases involving FieldChangeCallback timing. gonna change that behaviour and see if the community is still having the issue afterwards
Hello, I need some help, I have a script where I change an Integer triggered by OnPlayerTriggerEnter (Integer + 1) and OnPlayerTriggerExit (Integer - 1).
I am using Manual Sync ("[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]") to sync my Integer ("[UdonSynced] public int syncedIntegerValue;").
I set the owner ("Networking.SetOwner(Networking.LocalPlayer, gameObject);"), modify my value (+1 or -1) and RequestSerialization.
I then set the states of my local player as well as through OnDeserialization (based on my synced variable).
When I test this (using build and test) with 2 players where I have one player enter the trigger and shortly after exiting again while the other one is watching, I get the following sequence (Note: I added a bunch of Debug.Log messages to debug the issue.):
The player entering and exiting the trigger sees the following (Note: A trigger execution starts with the debug message "Synced"):
This is all fine and works as it is supposed to.
The player watching sees this:
Here, the "OnDeserialization" method is called twice, even though I "RequestSerialization" once.
Beyond that the values seem all wrong as well.
What it should display is this on enter:
And this on exit:
Since I cant find anything wrong on the coding end, I have a theory why this is happening, but I would like to confirm this with someone who knows better.
Since I am using Build and Test, I essentially have myself twice and once it detects that the player is entering (player 1 walking into the trigger) it shortly after detects that the player has exited (player 2 watching). I assume this is just some limitation of build and test, since I dont have anyone else right know to test this with in game. Please let me know what you think.
Update: Tested it with a friend and the issue still remains. Now I have no clue what I am doing wrong. 😅
Update: The issue was caused by having one script for enter trigger and one script for exit trigger on the same gameobject.
Best way to calculate latency? currently in my game world i have an UdonSynced START_TIME. This start time is set to the users system clock + a buffer. Players are reporting timers in games say like -60 minutes and stuff. So I'm assuming trusting system clocks is a bad idea lol.
I was thinking instead to use the Networking.GetServerTimeInSeconds() instead and then use Networking.CalculateServerDeltaTime() to find the latency?
That'd be sound logic. Never try to compare two clocks' times since NTP exists for a reason. I need to replace my CMOS battery since my time always keeps going off by quite a bit.
Just an update, this worked 🙂
If I put an udonsynced child under each object, could I set the child to owner in order to change variables related to that object without it affecting the parent?
I noticed today that syncing an "active" networked bool for an object makes its position sync when I move it, but I want only to sync the variable and not the entire object yet...
I want to be able to flag objects as active so another user won't use that instance, but it shouldn't be networked position-wise until an actual grab
I'd like to chat a bit about the video players I have it working but there are still some issues I run into. Most notably I'm receiving "player error" sometimes. I don't know what causes them so I don't quite know what I should do in response. I can try reloading (might be worthwhile to try). And once a person gets that error the video player no longer works. Even when I respawn the world it will not work. I have to restart the VRC app and that fixes things. Very curious behavior indeed.
ytdlp probably caches responses or results from URLs for a specific amount of time or possibly indefinitely which would make sense for the lifetime of the child process
Unless all urls are broken
In ondeserialization, it gives you a deserializationesult with sendTime. sendTime is a timestamp that is formatted into the context of the receiver's time.realtimesincestartup, so everyone will have a different number. But if you sync an offset like, say, 10 seconds in the future and then sync it, everybody can then add 10 seconds onto the sendTime and then they know exactly when it should happen, in their own time context.
I don't know what causes the failure on only some connections. And then it can affect them all. If something is cached that would help explain why creating a new instance would have no effect as the instance of the VRC app is the same. Do we know of a "reset" option on the player? Perhaps forcing a blank or known URL would work. I don't see anything like it in other VRC video worlds which makes me think it is due at least in part to my particular set up.
I do know of one world at least that goes back to a default playlist once a track finishes, but idk. Ytdlp / any other extractor VRC may use is pretty abstracted away
If the binary hasn't been updated in a while, could try replacing it with a newer version after backing up the old one ofc
YouTube did kinda recently do some stuff that broke a few extractors I know of
I don't see how each VRC app user can update yt-dlp but in any case it isn't needed or VRC would do it. I'm asking "how does it works" and "what do others do"? I don't see anything in the docs that suggests what to do when an error occurs. I see nothing in the code that suggests I can re-initialize or otherwise clear a fault condition.
I'm working on the idea that it is time-out condition and I've added a retry attempt. I will see if it makes a difference but I don't like shooting wildly into the darkness.
I also wonder if it could be due to the throttling that VRC has on various services.
Possible. But there shouldn't really have to be much for you to do. If there's an event for when a video fails to load then cool but you cannot do try catch blocks in udon so all of error handling in regards to ytdlp needs to be done on VRC's end.
I'm looking for answers more than guesses. Perhaps someone encountering the problem found a solution or knows what has happened when that error is thrown. We don't know if that is a failure to load that's the problem. I have to do something because the ability to show videos is removed from my world until I restart VRC.
Im trying to make a button that when clicked by anyone tells every client to run a function. It works just fine in play mode but in a build it does nothing for every client including the owner. For what ever reason, the SendCustomNetworkEvent wont fire, but other code will. Perhaps im misunderstanding how it works?
uuuuh i suspect that destroying smth that supposed to send network event might not be a good idea
It was just to test to see if it works, everything i've put into that second function wont get triggered. Even the client that pressed the button wont have the object be destroyed
I found out the issue has something to do with Instantiating the button. Placing the button in the scene manually will cause the event to fire. I'd rather not have to do anything like object pooling or preplacing a bunch of the buttons. Hoping I can still use Instantiate and somehow get the networking stuff to work on it.
Could someone possibly guide me in the correct direction as of what the best way to do this would be. I'm going to be syncing quite a lot of bools and shorts. Around 15 bools and maybe around 4-5 shorts. What i'm planning to do is have all the bools packed together and request serialization whenever one of the bools are updated and the same thing would apply to the floats. Or would it be better to have them be sent separately in different scripts instead of being packed together. I'm just wondering if it's better to send all the data together at once regardless if it needs to be updated or send the data seperately only when it needs to be updated
I'm just trying to figure out the do's and don'ts of networking xP
Correctly identifying when something needs to be sent or not is very hard to do.
It's highly recommended that you send the whole state each time.
Could I just check if the data has changed from the previous frame and if it has then send it?

That’s what my idea was for doing that. I’ve done a similar thing before on smaller projects
Networking.SetOwner(_localPlayer, gameObject);- Change the value.
RequestSerialization();
Well yee. I know how to do that. I’m just wondering if it’s better to send the data all at once in a single script or send it individually across multiple scripts
Each serialization is expensive
Optimally you'd want to have as few of them as possible
How often do these values change?
I'm not quite sure why you would need to monitor for changes to the data? That's indicative of some strange architectural choices.
So it wouldn’t matter then since again it would be sent the same number of times when a variable changes. These variables for the bools would be updated probably at max every few seconds but on average once every 5 minutes
just put it all in one script then
Well if I’m monitoring the change then I wouldn’t need to sync/serialize the data as much
Okay got it
You'll have late joiner desync.
Unless you specifically handle that.
Or, you could just use VRChat's networking how it's intended and let VRC handle all of that for you.
If you’re talking about continues I can’t do that as it’s too slow xP
Oh okay, then what are you talking about exactly?
If you only sync what you think has changed then the synced variables don't represent the whole state.
So a late joiner will only receive part of the data.
Okay I see what you mean
When someone joins the game VRC will automatically send them the latest version of the synced variables.
Mhm yes
Thank you for the help. I shall do that ^
If you have any programming knowledge and can get the process by ID, you can probably read the stdout/stderr to see for sure but lol 🤷
but point taken
Note : this Tweet has been deleted OwO/
Just in case, can you explain more on what you are aiming for ?
I’m trying to set up an object pooler by placing one instance of an object in each user’s spawner, so a user needs to flag that they’ve grabbed an instance and sync that they have it, but I don’t want to sync the actual positions until it’s picked up from its spawner
So I need some sort of synced variable stating that an instance is active without actually syncing the entire object, so it doesn’t sync it actually moving to the spawner
Oh I never replied lol ^^
So can I interpret it this way ? : You have several objects, that can be spawned ("position teleport") to a spawner and you want the position to be synced only when it is spawned
If I remember correctly, VRCObject Pool do take care the activeness of every object in the pool, so if they are inactive, they won't be synced, and when they are spawned, the object will be activated, and VRCObjectSync Script on that object will be active and thus syncing the position when it is spawned
Wait what 
Who posted that
Wait there’s a vrc object pool? 🤦 I’ll check that I think I’m stupid and tried recreating an existing prefab lol
Me, however, I have deleted the post way earlier, sorry for any inconvenience that I have caused.
Sometimes recreating something helps you understand more how it works
following a tutorial, I made an udon script that teleports players to a designated position when they enter a set trigger
is there a way to make it also toggle objects as well? I have a separate script for toggling objects but it's for buttons rather than walking into a trigger
I want to have it change the world's background music to another locally when you teleport to the second area
Connect some flow from VRCPlayerApi.TeleportTo to something that toggles objects like you normally would. Just without the need for the Interact event.
Keep in mind : while using OnPlayerTriggerEnter, make sure to make a branch to ensure that it is the local Player who is entring the collider OoO/
unless you know and are clear of what you exactly want
So I'm a little confused with what the best way to do this is
I want to make a card game where the cards are loaded in from an external source via string loading, to save me having to manually reupload the world to add cards, so I'm wondering what I do when it comes to making sure every player has the same cards, so I was thinking of the following method:
- Instance master creates instance and joins
- My code loads all of the card info and parses it into two UdonSynced arrays (for the different card types)
- When new players join, instead of them loading from the API, they wait for all cards to be synced over the network (which could take a bit of time as more are added)
- Boom, everyone has the same card
But VRChat syncing limits seem rather limiting if my game has too many cards
Would this work and be the "recommended method"?
I don't think it is necessary to sync using udon. You can simply download the string, and then create the cards in the same order on every client. Really depends on your overall setup, but that should be true for any imo
but what if i then update the cards and someoe joins an outdated lobby? the arrays would be mismatched
Okay, I'm having trouble explaining this properly without writing a wall of text. But maybe it's not necessary, could you describe how you are setting up your cards right now?
There are different ways to do it with different amounts of complexity.
-
GitHub stores two files that contain a list of cards (a file for each type of card)
-
I want to use that, instead of a textasset or array built into the world to make it easier to update
-
I was planning to separate those two into arrays of strings (one of which will also have a number associated with it, but for our purpose right now, not required)
-
When a player played a card, I would sync an integer that referred to the index of the selected card in the array, as opposed to syncing the whole string
-
If I updated the cards and a player joined after that, the array would be larger, and playing a newly added card would cause an "Index was outside the bounds of the array" error
You could include a version number in the data you download. Make sure the owner of the card game syncs the version number they're on when they started the game. If a user joins after you've updated the game, display a warning and inform them that their friends will need to refresh their cards to play with them.
seems useful, or if i wanted to be weirder, i could store the previous update's cards in an alternative branch in the same repo, and if the owners' cards are out of date, (will 100% of the time only ever be one update behind, vrc instances don't last that long), then load them from the legacy branch instead, and warn the player that the lobby is out of date and doesn't have the latest cards
not sure which is better ux-wise
This is more complicated, and relies on a faulty assumption.
i assume the faulty assumption is only ever assuming the version is one behind
Yep
If all you're worrying about is a mismatch between card versions, then that doesn't have much to do with how you sync your cards. You'd have to handle that separately. A version number is smart. You could even leave the new card white until you refresh the data where you overwrite the currently played cards with your new text and stuff.
Btw, I hope you are aware that you can't create URLs at runtime. All of them have to be predefined on upload.
yeah but the branch url could be hardcoded, like, replacing main with legacy in a new vrcurl variable
i could also just, query the latest card version on the master every like, 15-30 mins, and if there's a version mismatch, tell all clients to refresh via a network event before the game continues?
wouldn't be too hard and prevents unnecessary "this instance is outdated" stuff
and refreshes should take less than 30 seconds (accounting for latency between telling clients to refresh, and them all reporting they're updated), so
Sounds like you want to sync the card types? As I said, I don't think that's necessary. If I were to do it, I'd just make a big json of card types with their id/descriptions/etc. If a client gets a card type id synced that it doesn't have yet, that would be the clue to redownload the json again. Every time it gets redownloaded, I just overwrite the card types.
Just to make it clear: I'm differentiating card type ids that get downloaded and are used to generate cards vs cards that have a card type id that gets synced so other clients know what a card is displaying.
So like, CAH style is what format I'm using for my cards, where there's black and white cards, the format differs between the two because black cards have an identifier at the end that signals how many white cards it takes to satisfy the black card
So like, the black card format is
Some prompt, _____, and a text file.|1
_____ + _____ = _____.|3
Whereas the white card format is just, strings separated by newlines, like
Unity.
VRChat.
And there will be a 3rd VRCUrl that points to a version file that will just read something like
0.0.3-alpha
What I want to do is:
Every player loads the latest cards from the text files, and also loads the version into another string when they join
Every 30 minutes, the instance master queries that version file and if the version is not the currently loaded version, send a network event to all clients to let them know they need to refresh, and each client (including master) redownloads the black and white cards again, and non-masters copy the version number from the master (to save having to make a 3rd request)
Network events make more sense, since they don't need to be done for late joiners, late joiners will always have the latest version by design
Obviously in practice there's more to it (like waiting until a round is over before commencing with the refresh, to prevent some potential edge cases where the game could possibly break)
Ahh, I was definitely thinking about different types of card games.
However, I'd honestly still do it in a similar way I described before. I would not try to manage version as I don't think there is much need for that. Nor would I try to sync ALL the prompts for new joiners either. I would make it simple for myself and rely on a cyclical string download that would only add to the current pool of prompts. If I receive an id that's higher than my pool count, I simply redownload the prompts to display them.
Btw, pro tip: VRCJson is really fast for reading text files, if they get a bit bigger in your case.
what do you mean by "cyclical string download", you mean just, download them every so often and add the new entries to the list in the instance?
Yep. Even every 5 mins would be fine imo
If it's too much data, you could just make a separate url that tells you whether there was an update in your main json or not
i could also save a few seconds on card downloads by merging the two formats into just, raw json
instead of the format described up here, i could just do:
{
"blackCards": [
{ "content": "_____ + _____ = _____.", "requiredCards": 3 }
],
"whiteCards": [
"Unity",
"VRChat"
]
}
but the only way to tell that would be to know the current iteration of the card format that the game is on, which is only possible with runtime url construction, which you can't do, so i'd still need the version checking from the external file
No, what I mean is have 2 sources, one is the json with your data, one is a simple version number. In your udon script you reference both. You only download the version number. If it changes, then you download the json.
Either way, it's just ideas. I'm sure there are various ways to do it.
yeah, that's the same idea i had above when i was talking about this
and i'll test around and see what works
thank you for the advice! you've been very helpful :)
You would be a lot better off not accessing files directly, i.e. a static file host (like GitHub). If you can host a service instead you get file downloads with "logic" built into the service.
but still, there's not exactly any good way to do that, i can't send a request like https://mywebsite.com/cards?currentVersion=0.0.1-alpha because we don't have the ability to do that
Well, I'm not the best at explaining, but I hope it helps.
The cool thing about github is that it's whitelisted.
yeah, i would not like to have to lock players in a box that says
"TURN ON ALLOW UNTRUSTED URLS, THIS GAME DOES NOT WORK WITHOUT IT"
would be pretty shit
though they really need to rework the untrusted urls system
a prompting system would be so much better, like:
worlds have to ask for permission to access a list of hosts (or all hosts), and the player gets a popup that says
"Allow [world name] to access [url1], [url2], [url3]?"
"Allowing this world to access untrusted domains could be a privacy risk! Please make sure you trust the creator of this world before allowing access."
with like, a 5 second timer before you can press Allow
Do whatever makes sense to you but URLs are very common and most people have it turned on (I think) or are willing to. How many people do you see in the video playing worlds? And yes you can get the version number but necessarily in the direct method you displayed. Generally speaking nobody is going to know what version number to type in.
I hear all the chat so what privacy risks do you think I am open to that you aren't?
eh, vrchat has the untrusted urls system in the first place
fuck knows why it exists, worst case scenario someone gets my ip address (big deal, right? (not))
Because someone does something in the name of security doesn't make it a case for security. Nobody know where you live only the IP address of the "server" that issued you one.
meanwhile other platforms like roblox (despite only allowing requests on the server) allows you the freedom to make POST and GET requests as you please, without the limitations imposed by VRChat
It can be two cities away from where you live and it doesn't pinpoint your house.
vrchat has a lot of dumb security features, imho, they need to review it all
Is it possible to use it, or another way to download music into the world? In order to keep world size low with a lot of them
People usually just use video players for that
Yeah heard of that.
Then theorically, a good implementation could be two video players with video disabled, one to play, and another to preload the next song so that it loads in smoothly regardless of lag?
Since loading is I assume client sided
What are you even trying to do? There are a few caveats when using them and I'm not sure if you even want to deal with them.
Video players do sync their current time. And they do need to access the internet which can cause delay.
Pressing buttons to play specific music, with a big library, but id rather not have half my world size be those songs lol
Yeah, thats fine even if unsync, what i care here is that there isnt a 10 second delay. 1-2 is fine
So far ive got 2.5mb per song without using a player without compromising too much on quality.
My world being quest compatible itll add up really quickly
There are video player implementations that offer playlists. I'm probably the wrong person to ask since I rarely use them, but I chose this one last time: https://github.com/vrctxl/VideoTXL
There is also this which seems nice but I have yet to try it: https://architechvr.gumroad.com/l/protv
Sync and local video players for VRChat with design consideration for events. - vrctxl/VideoTXL
ProTV 3.0 is now in beta: https://protv.dev/news/protv3-betaReady-made video player solution for VRChat w/ Udon.Current LTS Version: 2.3.14Requires SDK3.0[Udon] and UdonSharp[U#] (last tested w/ v0.20.3 & 1.1.x).NOTE: This will be the LAST major version of ProTV that supports U# 0.20.3. ProTV 3.x and later will only support the UdonSharp 1.x rel...
Due to VRC running everything external through yt-dlp, you're unfortunately stuck with at least a 2 second loading delay on any external audio/videos, regardless of source. Kinda annoying.
thank you! Ill take a look later
Tbh it might be fine for anything thats supposed to play for long periods of time, like background music that changes depending on area
is there a way to update a sync value as often as the player bones update? Example I want to sync the VRCPlayerAPI.TrackingData of the players hands and head, but i want it to be updated in time with when the players bones are update, such that the position of where they should be is correct in relation to the avatar
I found I cant use a continuous sync because with what im doing it overloads the network, so it needs to be controlled manual, which i found syncing ever 0.2 seconds seems faster then the player updates (most of the time)
The IK update rate is 90Hz. Though you don't need to and shouldn't sync the TrackingData as VRChat already does this. Accessing each player's VRCPlayerApi within Update is more than sufficient for setting follower objects positions and rotations
I do this with a CyanPlayerObjectPool most of the time
If you wanted to get super in sync for some reason and not possibly waste CPU cycles, the bones are updated in FixedUpdate iirc because that's how FinalIK is. You should probably fact check me. To update after this, use a SendCustomEvent within a FixedUpdate to essentially act as a "LateFixedUpdate". Or VRChat may push Udon script execution after FinalIK making this unnecessary.
There is PostLateUpdate for that, if I'm not misunderstanding you.
Ah. Well if that's the case perhaps I got myself mixed up in the terminology 👍
Err no. LateUpdate is called after Update not FixedUpdate
FixedUpdate is based off Time.fixedDeltaTime
Also, it sounds like they want faster sync of positions than what VRChat is doing by default? I'm not sure if that is a realistic goal, since they might throttle even manual sync with more players and scripts syncing in an instance.
What they're syncing doesn't need to be synced though
PostLateUpdate
I don't remember tbh, but I do know it does work properly with bone positions etc
I just use Update personally so it's whatever, yes
I don't mind potentially wasted CPU cycles in this case. If I did, I'd make my own timer with SendCustomEventDelayedSeconds
Well, if they want even faster syncing for their stuff, you'd need to do your own syncing. But again, I don't think that's feasible anyways, unless you want to limit lobby sizes etc
Object ownership goes as fast as it can afaik
Even Udon manual syncing has a refresh rate
I wouldn't try to reinvent the wheel. At that case, I'd just suggest to VRChat to change the network spec if time is being wasted
Though I don't think VRChat is being developed to prioritize potentially trivial things like that as not every world is as time sensitive
problem with this is when the user is in desktop, the tracking data goes to the bone, not the tracking data
if it needs to follow the bones, then it obviously can be interpolated, remote player is never faster than 10hz
Can't you branch based off the VRCPlayerApi is user vr?
i can, but i wont have a solution for desktop
Just looked it up again, it did run after IK like you wanted to: https://udonsharp.docs.vrchat.com/events/#udon-events
- Udon Events
for vr its fine for the most part. The only thing that seems off it if im holding an vrcpickup it doesnt fallow the bone
So you are manually syncing pickups? And you have issues with positions and rotations on remote?
yes, this is one of the issues im trying to solve
There is a great asset someone made for that
Don't remember the name and I'm on my phone, give me a sec
i origonally was making them fallow and offest to to the bone, but they keep moving around
was it MMMaellon's smart object sync?
Yep
What's the use case for this
I have manually synced objects that i need their positions relitive to the hand synced, also i require a position that works for both desktop and vr for an object to fallow the hand
or "hand"
said object could be somthing like a blast or flamethrower effect for magic or what not
i hesitate to just look through this because i don't wanna just copy someone's work
Cant you just like store an offset transform relative to the pickup to spawn the effects at and make effects a child of the pickup if you need it to simulate in local space?
Just an alternative to the vrc object sync, it uses manual sync to flip states and let's physics do the rest when not held
the effects arnt a pickup, they are ment to be temp spawns
my items do somthing similar, i just havnt solved for how to have a desktop/vr solution for hand fallow
least not an optimal one
Yes. You misunderstand what I said. They can still be children of the pickup.
Nah just copy it lol, but their main script is pretty big and involved and probably not trivial to extract exactly what you want
what pick up?
The VRCPickup?
ah for the items, right the issue there is making an offset of the bone keeps changing
so it become inaccurate unless you keep updating the offset
Weapons have the VRCPickup. For example I have a gun and a transform which signifies the bullet exit. I can spawn effects at that offset
however, for the temp object spawn which doesnt have a pickup, there isnt an item to fallow
right, the way my item is structured there is a pickup as the handler, and a spawned item to represent the item
the problem is its manually synced, so i need where the item is supposed to be for others
Pickup as the handler?
yes
Ah so the script that's on the VRCPickup is manually synced?
ObjectSync wouldn't work then I see
ya, if i made it contious i couldnt do what i wanted to do with it
like making it switch to fallowing items, going two handed, swaping on physics, etc
also itd break the networking, cuz i have a lot of them
Cant you make the script a child of the VRCPickup and access the parent with transform.parent?
why not
but this still doesnt solve syncing
if the parent is contious sync for vrcobject sync, i cant have a large number of them
no, a player might have many of them
how many?
like one on each hip, a shield on back, several throwing kniefs, i cant just limit them
or if the game is somthing different, who knows
I understand your problem better. Okay
ya, overall its a system that allows a item to be any item in the system, so you dont have to go theres 10 of this item, and 10 of this item, etc
i have a pool of 300-500 that can become whatever item
tis on the excessive end, but it works
uuuuuuh, you do you but why would anyone srsly want to see it on a remote player?
You would still have to have a pool of manually synced objects since instantiated cant have network syncing
if you wanted to have a player who has swords, shields, and potions in an rp setting?
also, the items arnt per player, they exist in the world and can be manipulated by anyone
so you can pull a potion off your belt, hand it to your friend to use, that kinda thing
or you carry a plate with 10 slices of pizza on it to hand out and share
you spawn pickup when needed and show item per hand or smth
so its in hand while holding it
it can also be equiped to your arm, hip, or what ever
This asset also has some "stick" functionality I haven't messed with yet.
But either way, you probably want to sync the offset to the bones when attached, do some custom syncing when held (based on tracking data), and probably let the physics handle when thrown. I'd going to need some work to handle all the weird vrchat edge cases anyways
the vrcpickup was difficult to sync becuase i didnt want every object if someone was holding it to keep updating its postion
it made a lot more sence to update where it was held, and tell everyone to update its position based on that
less network syncing
the one problem with that is if desktop holds the item
overall it moves around and doesnt track to the hand
it tracks to the desktop tracker, which if i had that i wouldnt need to update its offset to maintain accuracy
You can at least instantiate a VRCPickup for desktop users so they get arm extending and that VRCPickup doesn't need to be synced itself
i suppose one thing to think about is my tracker sync probly only needs to care about desktop
Itd just be a local only pickup
im not fallowing
Maybe I'm confused. I'll just show myself out
I remember trying to solve similar issues before I found the smart object sync. Sorry, but it's been a while since so I don't remember the details ATM. I do know that you have to sync an offset to tracking data and stuff
hmm...i suppose somthing i can do for desktop only is somthing like...instead of syncing the postion/rotation just sync the offset of the traker to the bone
in that way, when somthing is trying to get the position, it will be relitive to the already syncd bone rather then a point in space that may or may not be ahead or behind the model sync
Yep, something like that. I'll let you know if I find the code and remember more.
thanks!
hi @mossy ruin! ive had the same issues for my new project, i ended up discarding vrcpickups and making my own pickup system, but for desktop tracking specifically, im simply using head position and applying an offset position
(you wont be able to get the hand reaching out effect, i dont think it will be possible with instantiated pickups in general)
still WIP: (also using cyan player pool but this is a separate script)
[NonSerialized] Vector3 desktopPosition = new Vector3(0.175f, -0.15f, 0.35f);
[NonSerialized] Quaternion desktopRotation = new Quaternion(-0.5f, -0.5f, -0.5f, 0.5f);
[NonSerialized] VRCPlayerApi.TrackingDataType head = VRCPlayerApi.TrackingDataType.Head;
[NonSerialized] HumanBodyBones rightHand = HumanBodyBones.RightHand;
[NonSerialized] HumanBodyBones leftHand = HumanBodyBones.LeftHand;
void Start() { }
public override void PostLateUpdate() {
if (Owner == null) return;
transform.parent.position = Owner.GetPosition();
if (inVR) {
rightHandTracker.SetPositionAndRotation(Owner.GetBonePosition(rightHand), Owner.GetBoneRotation(rightHand));
leftHandTracker.SetPositionAndRotation(Owner.GetBonePosition(leftHand), Owner.GetBoneRotation(leftHand));
} else {
VRCPlayerApi.TrackingData headData = Owner.GetTrackingData(head);
rightHandTracker.SetPositionAndRotation(
headData.position + (headData.rotation * desktopPosition),
headData.rotation * desktopRotation);
}
}```
getting the head trackingdata of the desktop user just gets the head bone position/rotation right?
main issue with that is if the user looks straight up, their head bone does not tilt to look up enough
let me test that real quick
it was the issue i was running into, when i pointed straight up it doesnt go up enough, causing a desync in where the user is aiming
but do you need that data?
yes, an example i had was a temp item spawn that fallows the players "hand"
if i use the head data, the user can only look so high, and cant aim at things directly above them, or at least it doesnt aim high enough
i forget the degree of angle, but its enough to be frustrating
it was a solution i used within my dragudon ball z world, it made hitting people who flew above you impossible
in the case of that system it was firing a partical to hit by the target, however its irrelivent for the current system. Currently the issue is if i wanna use the system for a magic system to let someone do a flamethrower spell
the spell wont spray above a certain threshold, which is not accetable
because then you cant direct it straight up
anyways, i have a solution to try, so im going to see how itll work, thanks tho
Ah, it turned out to be more simple than I thought. All you have to do is sync the offset of bone to pickup. Then on remote, you just reposition your pickup by adding that offset to your bone position.
i was just finished testing somthing similar to get the tracker positions
i currently for desktop am syncing the headrotation offset from the head tracker, then basing the left/right hand tracker off the head tracker
so when i get it, i get the tracker rotation with the offset, then get the hand positions/rotations based on their offsets from the head tracker
Problem is, tracking data is tracking data locally, but bone data on remote. So you have to base everything on bone to be more accurate with it.
and turns out syncing it 5 times a second seems to be enough to look smooth enough
ya, what im syncing is the offsets of said tracker
so i already have an player object pool where everyone owns their own object which i use to send data through
i just added the tracker sync onto that
and since all i need to sync is desktop, its only activly syncing if there is a desktop user
Hmm, hopefully it works well for you then. If you find issues, the bone method is a lot more simple and should work regardless of VR or desktop.
the only issue youll have with that is with the headbone
the headbone does not rotate vertically beyond a certain point, so the head tracker even locally will be different
Do you have an example of something that NEEDS to be attached to the head and then looked up with?
That's pretty cool. And yeah, I personally don't think it needs to be attached to the head, but I can totally see why you'd still try to.
why the left and right hand wouldnt be attached to the head?
its because thats where the players left/right hand are in desktop
the same usecase in vr is that it comes out the hand
Not sure what you mean. I was just referring to how there are other ways to solve it like make the spray into a pickup.
But making it go off from your head is a different feel I guess.
so if you pickup and object, it uses the local hand track for where the item goes far as i can tell
i plan to test now i have a sync trackingdata position/rotation, but its to simplfy where the hands/head is
when picking up an object, desktop has it move around, before i was just syncing the offset based on hte bone, but i have to correct it several times
if i just base the offset on the tracker, i dont need to correct it and it leaves less syncing by item
also itll look smoother then correcting it several times
What do you mean by correcting? If you sync the offset position and rotation of the bone, you don't need to do that afaik
so when you pick up the object, the frame you do your hand is to your side
From what I gather your problem is that if you attach something to the head, you can't look up fully on remote?
so if i wanted to sync it by bone for vr and desktop, i need to correct it as the desktop hand moves to "grab" it
and while running around, the item continues to move around, and you cant base the offset on the bone of the head or hand because it keeps moving compared to them
this offset is needed to sync it for remote
but ya, also, if i base somthing on the head for remote sync, then you have limitations when the player looks up
id prefer to have no limitions and the system to work intuitivly and quickly
oh i should mention, i have 3 types of handlers for my item spawner
Ah, that's true hmm. Why does it have to be so difficult lol
type one is what is typical of vrcpickup, with items that hold their own data and have the player take ownership. Type 2 is ment for more idle or evioronment spawned items which has a manager owned by own person which handles their data (ment for very low interaction). Then temp items, which are ment to spawn and then disapear, so they just have local data with no set ownership as they are ment to go away
the blizard in the gif was a temp item, so i can cast a spell and it shows the spray
this is another example https://gyazo.com/cba29d511a044e6d70291281224112a7
spawns a pickup which you can grab and interact with
its jerking around because i still have code in there trying to correct it XD i need to work on it tomarrow to remove that and see if its still good
I mean, it looks good so far, no need to overdo it with the accuracy.
I really like spells and dungeons so I'm actually looking forward what the end result will be.
Am I correct in understanding that this means a serialization request was rejected because the requester was not the owner?
Yes thats how ownership is supposed to work, only events can be called from other clients
Yeah just wanted to make sure I was able to single out the log that shows that
now if that disappears, and my log doesn't appear, I'll know to scream :3
that actually showed I wasn't receiving anything correctly, so thanks alot!
I am making a menu with join and leave buttons to set the player list, but it seems to be out of sync, need some advice.
You need to beware that you put button SetActive before you actually check for condition whether a player can joined or leave or not. This will cause the button state to change regardless of joining result, and joining by other player will not update the button state.
Oh, that is indeed another problem to be solved, Is there any script for me to refer to? I think this type of coordinating players to participate in the game is quite common...
If you could explain "out of sync" actually does, that could be a clue lead to where the fault is.
The first client clicks the join button and the second client cannot see the list update
What is the purpose of allNames variable?
to display join list on ui
Did you assign its value anywhere?
I thought it would be stored on playerNames
I saw it update the variable after being deserialization, but does that trigger UI update?
for local player, yes
I don't see any function that does update text UI.
My idea is that each join or exit function will update string[] → print to text[] through deserialization so everyone can see it on ui.
But you didn't have anything to display it so how did you how it's out of sync?
how can i manually update a sync var by code? i cant find anything in the docs
Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
{
RequestSerialization();
}``` would this sync all synced variables on my script?
On only the object owner can make update to sync variable. RequestSerialization means owner wants to send out new values for variables.
im trying to do something where a joining player will try and sync its var with the owner's
Joining player will automatically get variable marked as synced. And this will trigger OnDeserialization event right after they got all the synced variable.
with manual synced method selected, right?
Both manual or continous work the same way.
this doesnt seem to work for me
How did you know it doesn't? Explain how you test it.
ill explain what im doing in more detail. im making a day/night cycle and want to sync the time of day for all players. The owner of the script does RequestSerialization(); at the beginning of the day which works. I test it by building and testing with 2 clients, one client always joins the world later and the time of day is noticeably out of sync (i have it sped up for testing) untill the owner reaches a new day with fires RequestSerialization(); and syncs all players up.
Explain the variable that you used for syncing.
Like how you used it for syncing day/night cycle.
float dayProg = 0.25f;```
and this in update()
``` if (dayProg > 1)
{
if (Networking.IsOwner(gameObject))
{
RequestSerialization();
}
dayProg = 0;
}
dayProg += Time.deltaTime / dayLength;```
dayProg supposedly have to be updated in realtime. But it sync only when day changed. Therefore, other player would only see dayProg value as >1 (because RequestSerialization happens before you change the value).
The syncing every new day part works. it updates and checks > 1 locally. syncing on join doesn't. i would work around by the owner checking for new players and firing RequestSerialization(); if someone joins
It doesn't work because you're using wrong logic.
You have 2 choices:
- Use continous sync to do realtime update on dayProg. Or
- Use manual sync to sync time of start of day relative to server time. Then calculate day prog from start of day time locally for every client.
i tried continuous sync and it looks very laggy. ill try my work around.
is there a OnPlayerJoin method?
To explain your logic, you tell it to remember time at start of day, which is 0, then you progress it normally. And then other player joined, the program still remember the time need to sync is 0, so it send out this data to the joined player where they also think it's just start of the day, except you on your own had progressed it so far.
There is. But I reckon again, that isn't your solution.
so if the dayProg would be 0.3 for the owner and i do RequestSerialization(); all other players would receive 0 as the value because i missed a step?
If you RequestSerialization after you changed it to 0.3, every player, will get 0.3. But once you had progress it for some time, to 0.8 for example, once a new player joined, the new player will get 0.3, because that is the value you tell it to give to everyone.
oh ok, so ig i would need something like
{
if (Networking.IsOwner(gameObject))
{
RequestSerialization();
}
}```
it would tell everyone including the new player to sync to the owner's time
I'm going to doubt its reliability.
ill test it, thanks for explaining this
A couple of items worthy of note... if @humble girder offers concerns about reliability it warrants concern. Also there is almost (like 99.99% of the time) no need to be tricky with code. The best code is straightforward and obvious and as a side-effect it works. If one has some odd behavior it is surely due to a mistaken understanding of how things work. I wouldn't (for instance) update a property marked with UdonSynced and not actually sync it. You are asking for weird unexplainable behavior and then you add more code to counter the weird code. And then repeat 🙂
does requestserialization sends it at the very moment or at the end of frame
should probably have pinged that.. @lusty pagoda
so if i spam multiple times at the same frame it doesnt change anything with networking
or run it and still running my own code in same frame
it will wait for me to finish
yea, you can change your synced variable as many times as you want in the same frame after you've called request serialization and whatever the last thing you set it to will be networked
no i meant multiple requestserializations
ah, doesn't matter it just does it once at the end of the udon execution
no matter how many times you call it
it just requests serialization, it doesn't do serialization
Im having issues with a delay in synced variables. An example would be:
button clicked -> send event to owner -> owner modifies synced variables -> send event to all clients -> clients then used modified synced variables
Im using a continuous sync method on the object that the variables are stored on. Would changing it to manual be quicker, or are there methods I could use to reliably wait till the synced variables have been sent to the clients to then trigger the events?
@lusty pagoda not end of frame, but rather at the next network tick, which is 20 times per second. The rest is correct though, multiple requestserializations will only ever trigger one serialization
didn't know that part, that's also really good to know
It still hits the rate limiting code even though it doesn't actually serialize.
So don't call it every frame.
so not during udon frame
sorta related to this, but what's the timing with networked events and variable deserialization?
not connected
There is no fixed timing. Its basically up to the intermediary proxy server that will pull & push data whenever it does.
Generally you should try not to design for a specific interval, but instead imagine that any network synchronization/action could potentially take several seconds (even though it usually only takes a few hundred milliseconds)
soo.. never rely on data being synced with networked events, but you can rely on other data being being synced on deserialize?
i found out best way of networking is performing deserialization on sender
so everybody runs exact same function
Not sure exactly what point youre making there, but yes I guess?
Go slower, and eventually stop working.
gotcha thanks been trying to figure that out for the longest
so for local buttons what do you recommend?
Just None
what about global pickups?
vrc object sync is forcing to use continous mode.
ohh gotcha
what about objects that are only locally seen but have a global effect?
You better take a focus on what need to be synced. If sync variable is rarely update, then use manual sync. If synv variable is often sync for small amount of data, every frame, then it's continous.
gotcha gotcha thanks!
actually one more question , how will i know when theres too many syncs? is there like a counter somewhere?
Use property Networking.IsClogged to see whether there are to many data transfer going on.
gotcha gotcha
yeah because ill have like a lot of people in the world and that starts making a few things start slowing down and stop working
Anyone know why the sdk throws errors when importing new network id's?
Also throws id error on build. Both pc and quest scenes are identical. Using separate projects both on unity 2022.3.22f1, latest sdk version.
Pls ping me if you might know why
hello, does anyone has an example of how to request ownership of an object using udonsharp? i do Networking.SetOwner(Networking.LocalPlayer, gameObject); but OnOwnershipRequest is not being triggered
oh thanks!
comment dissapeared
nah i think i wrong
OnOwnershipRequest is used to potentially block the transfer. If you just wanted an event for when it's actually transferred, OnOwnershipTransferred
The OnOwnershipRequest is only run on the owner's side
does ownership transfering is something that i can test in the simulator inside unity?
i tried deleting OnOwnershipRequest and OnOwnershipTransfer is not being triggered either
so i have no idea what am i missing
ClientSim should be able to do that. You'd spawn another player.
OnOwnershipTransferred(VRCPlayerApi player)
Not OnOwnershipTransfer
Also keep in mind that the event is run on this Object which had its ownership changed. You can't do something like a blanket global listener
You can entirely remove the OnOwnershipRequest since you always return true
tried doing that and also OnOwnershipTransfered is not being triggered either
so im not sure if im missing something
Then it's likely you'll need to run two instances of the client to fully simulate the network just to be safe and sane
being sane sounds good uwu, thanks for the help, let me try it
it is working in the standalone clients, thanks
OH i think that the problem is that the master of the instance already owns the object, so onOwnershipRequest is not being called, casue i already own the object
Is there a way in the vrc simulator to switch places with a spawned player?
Like, control the remote player insie unity
hey guys could i have a little help, im trying to create an object for each player that joins and given them ownership of it so i tried doing this but it deson't work, could i get help with what im missing thanks!
you cannot instantiate networked objects, use vrc object pool. theres also https://github.com/CyanLaser/CyanPlayerObjectPool
So I'm just dipping into udon networking for the first time and I'm trying to make sure I'm on the right track:
If you're dealing with logic for a game on a single object, the correct way to update the game state is by having each player take temporary ownership of that centralized object whenever they want to request serialization for their contribution (e.g. updating score), right?
My original intuition was to have that object under the exclusive ownership of a single "game master", and have players send custom network events to them so that they can update all the logic and request serialization in a centralized way - but I'm guessing that's not the practical solution, right?
cant you just have objects per player?
overall yes ownership and serialization is better for important and long lasting data
objects per player is interesting, I hadn't considered how to structure it that way yet - I'll have to consider how to refactor it that way and see where that leads me, thanks
i mean just everyone updates their score when needed and when its time to format a table it just locally reads data per player. but ig it depends on what it looks like, im thinking of prison escape lobby
Yeah you can technically write things from a “peer to peer” perspective since each player can own their own object with a set of synced variables for their personal values, then people can just use data from the selected player/players when needed
https://github.com/CyanLaser/CyanPlayerObjectPool is a good start on that since it handles the ownership/reassignment of active player objects for you
I see, yeah that makes a lot more sense than what I was trying to reason out, appreciate you both
I should take my own advice and decouple/detangle some of my own master stuff at some point lol
Late joiners kill running games in my game atm 😂 I think everyone runs stuff master should be managing because I gave players too much ownership over the start-game handling
Note: make sure you don’t end up telling every player in your game to try and start the game for every other player when you click the start game button 👌
That’s gonna take me hours to figure out for sure 😭
I could’ve sworn I’ve been to a world a that culled remote players outside of a specific room. Is this still possible from the world creator end? If so, can someone point me in the direction of creating that sort of logic for a player occlusion zone. 🙏🏻
Goal: I’d like to be able to hide players sitting at spawn so that players loading in can watch an in-game “cutscene”/ animation without others clipping into it lol
How about teleporting them to other random place and have the animation object go with them?
Yeah simplest way might just be teleporting players with a trigger box in the area that activates on player joined 😆
will a OnDeserialization trigger when you load into the world if the synced property hasnt changed from the default starting variable, or does it always triger on load into a world?
After a quick little test it appears that is the case
request serialization exist just for that, it would never sync itself
It does indeed. When you join a world all network data is sent to you, which gets deserialized. No matter what.
i am testing udon as well, im very close to do something and you can not send stuff to objects you dont own, so game manager would need to be watching the changes of the objects owned by other players and react to that
i just finished a small board game with udon, but i dont know how to handle player disconections at mid game to properly reset the table, i see "OnPlayerLeave" in udonsharp, but i dont know what can i do with it, in my mind, only the owner can modify the object, so onplayerleave means the owner already left, so i dont know what to do to reset the owned objects back to the initial state
Can someone recommend me a getting started with networking guide that isnt by vrc? I couldn't follow that one well
Quick clarification... if you are talking about synced variables nobody is actually sending and similarly nobody is actually watching. Data is sync'd and everybody is informed when the data changes.
It is best to re-read the docs (multiple times as necessary) but when a player leaves another play is made the owner. All game objects are owned by someone. So the owner "changed" when the player left. You'll need your own logic to resolve it.
VRC uses the Photon library, still using Photon Pun I believe but don't quote me on that. You can familarize yourself with how Photon works but we don't have access to RPCs directly in Udon. If you are going to write networked Udon code you will have to learn Udon networking conventions there is no way around it.
I dont want to be rude but your answers are not helping anyone =3
I don't know what that means but sorry.
Probably more of an architectural thing than networking. Adding on to what Tom said about the ownership being transferred, you could re-run what you used to set the board game up after the ownership has transferred OnOwnershipTransferred(VRCPlayerApi player).
I read that you don't have such a function to reset, a good step would be to store the initial board state as variables to reference later like in this situation. For example, their positions on the board.
I know nothing about your setup and implementation so try starting here
is OnOwnershipTransfered called after an object owned after a player left and owned an object? i added logs and at least in the clien sim i dont see them in the console
built & test with 2 clients
The ownership is transferred to the network master. If the master was the one who left, then it's to the new network master. That event should fire, but ClientSim isn't good for network testing.
so, i'm making a system where: on start, a timer will begin counting down, and when it reaches zero a duck from an object pool spawns. i want the selected duck that spawns in to be the same for every player, and i want the same for the timer. how can i do that? this is my current script:
[Header("- Variables -")]
[SerializeField] private VRCObjectPool ducksPool;
[SerializeField] private Transform duckSpawnLocation;
[Header("- Options -")]
[SerializeField] private float duckDelay = 120f;
private bool timerActive, poolFull;
private double targetTime = 0f;
private void Start()
{
SetTimer();
}
public void SetTimer()
{
Debug.Log($"<color=purple><b>{this.name}</b></color> <b>Timer was set</b>");
if (timerActive) return;
timerActive = true;
targetTime = Time.time + duckDelay;
RunTimer();
}
public void RunTimer()
{
float timeLeft = (float)(targetTime - Time.time);
timerActive = timeLeft < 0;
if (timerActive)
{
SpawnDuck();
timerActive = false;
SetTimer();
}
else
{
if (poolFull) return;
SendCustomEventDelayedSeconds(nameof(RunTimer), timeLeft % 1);
Debug.Log($"<color=purple><b>{this.name}</b></color> <color=white><i>There are {timeLeft} seconds until duck</i></color>");
}
}
public void SpawnDuck()
{
ducksPool.Shuffle();
GameObject duck = ducksPool.TryToSpawn();
if (Utilities.IsValid(duck))
{
duck.transform.position = duckSpawnLocation.position;
}
Debug.Log($"<color=purple><b>{this.name}</b></color> <color=green>Duck spawned!</color>");
}
also, i have "poolFull" created for when every object in the pool has become active. i haven't set that up yet though, cause i'm not sure how i'd check if all object pool objects are active or not
(each duck object also has a VRC_ObjectSync)
(don’t think that relates but might as well mention it)
I don't see any networking code which is likely going to cause you issues. My first thought is that I wouldn't have every client set a timer and act when the time has elapsed. Might it not be easier and more accurate for one player to manage the timer and then have that person post an event that all players can just react to?
I don't know what you want to occur when a late joiner arrives but note that they would start their own timer using that code and as a result wouldn't display the duck at the same time. They would if they were present and an event was used.
i was thinking that i would have only the owner of the object control the timer. i just haven’t added any networking yet because i needed some pointers first, as this is different from other things i’ve networked, since it has a timer that controls an event
I thought about it a bit more and there are issues you will need to solve. It will almost certainly require a synced variable indicating which element the players should spawn. You can't shuffle them on each client and expect the same results in each client. You (seemingly) don't need a pool but it depends upon the effect you are looking for.
what i’m looking for is to have the owner control the whole thing, so that one person runs the timer and spawns a random duck. and i want the time that it spawns to be synced, and the chosen duck to be synced, preferably for late-joiners aswell which is why i have an ObjectPool, because “the objects are enabled or disabled for late-joiners where appropriate”
Ah that makes sense. I think you are going to need some network code to prevent every player from running Start() on this class.
would a simple if (!Networking.LocalPlayer.isOwner) return: line work?
That should work. I have a suggestion since you seem intent on using elaborate log statements.
Debug.Log($"<color=purple><b>{this.name}</b></color> <b>Timer was set</b>");
Create a static class (name it Logger or something close) and add static methods to it that accept some message text "Timer was set" in this example and have that method customize the layout and call Debug.Log. You would pass both the "name" and the message in the example above. You can create lots of variant methods if you want some green, red or whatever. You can even create methods for Bold() and such. Typing all the html directly into the "app" is way too much work and can obscure the purpose of the code. You really don't want to be debugging mismatched HTML markup. 🙂
that’s an amazing idea
i just like the colored text, and it separates my different logs more too
I understand the purpose. I use colored messages as well. I have methods that handle the coloring and I don't specify "purple" for example but rather use const strings that indicate severity. So debugcolor, infoColor, etc. You can expand on that. I simply Logger.Trace something and would get the trace color and the message in trace format.
Logger.Info($"{_nameofClass}.{nameof(Start)}");
That will have the Info color and if I want date/time or some tag that identifies my app-centric log messages it is all handled in the Logger class.
I’ve always been afraid to ask this question because I feel like an idiot because I think I’ve asked it like a bunch, but I never wrapped around in my head correctly. Are we able to just make a class static and then reference it from other classes, and that works? Years ago I tried making a static class and it didn’t work and then I remember hearing that we were supposed to be getting support for it and thinking that it was part of Udon 2?
@stone badger Specifically off of your conversation above ^
Sometimes when I see people say that in here, I wonder if they mean something that people call static but it’s a workaround, and I’m not actually sure if they’re just mean a real static class
You should think of it more like clarification than "idiot" 🙂 Sometimes taking information in a different form (or repeated) works. A static class has a definition in languages that support them (like C# for instance). The class just exists. You don't instantiate one.as you would a regular class. There are some design considerations as when to use a static class but we don't have dependency injection (for instance) in VRC/Udon so a static Logger works well. Best thing (I think) you can do is to create one in a project you are working on and just try it.
Also, Udon# for U1 does support static classes kinda. It more or less just appends the methods used to the bytecode of the programs that use it. This implementation isn't ideal, but it works™️
This makes complete sense, as long as I know that it compiles some thing out of that word when I make a class and it doesn’t just tell me it won’t work 😂
Gonna have to get into the headspace of statics because I worked myself out of those when I learned they wouldn’t work here
I would caution against going overboard on static C# classes. I have very few and they serve purposes that wouldn't be nearly as easy to duplicate with an UdonBehaviour-based class. The other useful class are "singleton" (I don't mean in the Singleton-sense) classes. They can exist in the world to handle lots of utility-type settings and such.
So I have an object that does something to the player on grab. When the player drops it, it undoes those changes. But the issue I'm running into is how to handle the item being stolen, because the way I grab the "current grabber" is by checking the owner, but if the item is stolen then the deactivation will end up firing on the stealer as it'll run, the owner will now be the person that stole it and obviously that's not going to work. Can anyone think of a good way to handle a deactivation on something like that?
Store the owner on grab and then use it later.
Alternatively, you could disable stealing to make it easier.
I have already disabled stealing apparently, so the issue I assumed was due to stealing was not. So... one less problem, one more problem, lol. But yeah, storing properly makes sense.
i can autosuggest any link even dynamic one right
id like to user paste in any link and after confirmation edit it out and display another prompt with modified link
can i do that
URLs can only be edited during runtime by the player I think
@lusty pagoda is your question about building suggestions off of partially inserted text or inserting the selected suggestion into the url field? Those are two different things happening to solve your question
(The middle step of comparing the retrieved text to an array/database and returning a related suggestion is definitely doable on udon)
maybe you can use one of these events to get when the player finished typing, then comparing the input field url text with the suggestions you defined at editor time. if one of them match, you just have to swap the url at the input field
im not sure about that too
also im not sure if this question should be at udon-networking...
Good point lol
whats the time limit for string loading
so this does work just fine, having an IsOwner check at the start
the only thing i need networked now is the progress bar, because of course, it only updates for the owner
should i try and sync the slider's value from that script, because it's what controls the value, or could i make something and attatch it to the slider itself to network the value?
ok so how i open input field with udon
i managed open it up but no idea about autosuggestion
inputfield.text inaccessible and inputfield.textComponent.text does nothing
ok so exact example of what im trying to do
-> somebody pastes https://youtu.be/dQw4w9WgXcQ
after he hits enter, take dQw4w9WgXcQ and construct new link with that included, and autosuggest it for player so he just hits enter
Is it that bad if I recognized that link?
yes
ah i see
seturl is for suggesting link
but then its useless because i already have vrcurl in that case
i thought suggesting can be done without player input
well whatever i can make server handle that as well
I have observed that for some reason, the play audio part of this udon graph is synced between players, but the rest of it isn't what's wrong with it?
Hi
Picture is too small to make out any of it
I thought everyone recongised XcQ by now.
I recognized it by dQw
whats the limit of udonsynced variable size
and can i load just variables i want not all variables
I sometimes see this code in prefabs.
if (!Networking.LocalPlayer.IsOwner(gameObject))
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
}
Is there a reason to do that instead of this?
Networking.SetOwner(Networking.LocalPlayer, gameObject);
Does SetOwner not check if the player already owns that object?
If you're already the owner it posts a warning message in logs. But it doesn't cause any unnecessary traffic so it's not the end of the world. I guess people just like to avoid that warning message which is fair
Each manual sync behavior can sync toughly 280 thousand bytes
No
Oh okay! That's good to know
Is this properly synced? it looks like it to me, but i just tested in-game and it didn't work
[UdonSynced, FieldChangeCallback(nameof(RNDNum))] private int rndNum;
[SerializeField] private float frequency = 10f;
private ParticleSystem particles;
void Start()
{
particles = this.GetComponent<ParticleSystem>();
}
private void OnEnable()
{
if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject)) return;
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), 0.5f);
}
public void RandomlyPlay()
{
RNDNum = Random.Range(0, 20);
if (rndNum == 10 && particles.isStopped)
{
particles.Play();
Debug.Log($"<color=lightblue>{this.name}:</color> <color=green><b>Playing!</b></color>");
}
Debug.Log($"<color=lightblue>{this.name}:</color> RNG was {rndNum}");
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), frequency);
}
public int RNDNum
{
set
{
rndNum = value;
}
get { return rndNum; }
}
What didn't work? Who sees the value of RNDNum other than the owner of this object?
You don't RequestSerialization after setting RNDNum so it won't sync the new value
All supported attributes in UdonSharp
ah right, forgot about that
i didn't think i had to though, since i used the equivalent of OnVarChanged
what didn't work, was both clients didn't see the particles play
Do you have some other script that reads RNDNum and plays the particles for other clients? The script you posted will only play particles for the player syncing RNDNum since non-owners never run the RandomlyPlay loop
Seems like you'd want to do something like:
[UdonSynced, FieldChangeCallback(nameof(RNDNum))] private int rndNum;
[SerializeField] private float frequency = 10f;
private ParticleSystem particles;
void Start()
{
particles = this.GetComponent<ParticleSystem>();
}
private void OnEnable()
{
if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject)) return;
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), 0.5f);
}
public void RandomlyPlay()
{
RNDNum = Random.Range(0, 20);
RequestSerialization();
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), frequency);
}
public void UpdateParticles()
{
if (rndNum == 10 && particles.isStopped)
{
particles.Play();
Debug.Log($"<color=lightblue>{this.name}:</color> <color=green><b>Playing!</b></color>");
}
Debug.Log($"<color=lightblue>{this.name}:</color> RNG was {rndNum}");
}
public int RNDNum
{
set
{
rndNum = value;
UpdateParticles(); // Plays particles for owner when updating sync state and non-owners when sync update is received
}
get { return rndNum; }
}
Also keep in mind that if the object owner leaves, your state will stop syncing since nobody will be running the RandomlyPlay loop
You'd probably want some extra logic to handle the owner leaving in OnPlayerLeft
the object with the script is a child to a pickupable parent, so wouldn’t the owner be set when the object is picked up?
and thanks for updating the script to make it work, i’ll try it later
There seems to be a bit of ambiguous behavior from what I can see. You essentially have a Boolean condition (not an integer). I see the random number but aren't you only acting when the number is equal to 10? That is a Boolean condition and updating the number repeatedly may not be necessary. Is it that you want a 1 in 20 chance of the particles playing?
If you're using VRCObjectSync for the pickup I doubt it will take ownership of the child gameobjects automatically, haven't tested it myself though so you'd have to confirm that
But if your child object is disabled and reenabled every time you pick up the parent object that part should at least handle rechecking the owner, just gotta check that is owner is who you are expecting it to be
You could always called Networking.SetOwner on the child object in the parent's OnPickup event to make sure it passes
yes exactly. i want a 1 in 20 chance for the particles to play every “frequency” seconds
that’s all i want the system to do really. when the object is enabled, start sending events every “frequency” seconds, and if a 1 in 20 chance happens, play the particles
(and for the particles to be synced when playing)
actually, now that i think about it, i can just use a network event…
it isn’t something that needs to be synced over-time, it’s just a short burst-like effect
Then I believe you have made it too complex. There is seemingly no reason every player needs to know about the failure values. The owner can handle the logic and simply instruct other players to "play" not to think about playing. I don't know what you mean my synced particles exactly. They will all play at the same time but at the particle by particle level. And any networking must take into account what happens when a late joiner appears.
for a different system, how would i do this? i have a different script in the world which only the owner runs, so if they leave i would need to re-assign it there too
Something like this should work, thinking about it more OnOwnershipTransfer would cover more cases than OnPlayerLeft:
private bool IsEventActive;
private void BeginEventLoop(float frequency)
{
if(IsEventActive) return;
IsEventActive = true;
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), frequency);
}
private void OnEnable()
{
if(!Networking.IsOwner(Networking.LocalPlayer, gameObject)) return;
BeginEventLoop(0.5f);
}
public void RandomlyPlay()
{
// Stop looping if ownership is lost
if(!Networking.IsOwner(Networking.LocalPlayer, gameObject))
{
IsEventActive = false;
return;
}
RNDNum = Random.Range(0, 20);
RequestSerialization();
// Keep looping
IsEventActive = true;
SendCustomEventDelayedSeconds(nameof(RandomlyPlay), frequency);
}
public override void OnOwnershipTransferred(VRCPlayerApi player)
{
// Start looping for new object owner
if(player == Networking.LocalPlayer) BeginEventLoop(frequency);
}
anyone here know how to do some vrchat network diggin for clients using something like wireshark or similar? looking to figure out who drew something using pens, externally
There isn't any client-to-client communication at the physical level (although there is at the logical level), and I am confident all client-to-server comms are encrypted so you can't spy on them.
🤮
I just did a local test & confirmed a new player loading in didn't kill an existing game w/ 10 frames of delay, but is this realistic? Should I just make it a few seconds just in case and put up a wall like people always talk about, or is it alright to stick with a tiny delay like this?
(my issue is that IsPlaying is serialized so no delay kills all the swords lol)
Is there still a way to get the instance creator? (not the master)
get player by ID. It always either starts at 0 or 1. I forget which
And each player increments the ID
Or well. The instance creator doesn't always have to join the world and can join the world later, but getting the instance creator isn't available afaik
You cannot access user data unless they're in the world
I dont need their info while they're outside the world, just if they create it but dont join first (IE drop a portal and herd a few people through and then follow)
I believe you can run local checks using Networking.isInstanceOwner. This is different from instance master
Ahhhh yup that was what i was looking for
https://creators.vrchat.com/worlds/udon/networking/network-components/#networking-properties
This doc covers Networking Components, Properties and Events you can use in your Udon Programs.
Although I have a feeling the instance owner behaves differently when its a group instance, ill have to double check that
A quick question of my own, hopefully an easy one- I'm making a vending machine. last time I tried to instantiate a prefab with udon on it during runtime, it wouldn't sync properly. Has this changed? Or might I be better off with a bunch of disabled objects in a pool and enable and disable one by one instead of instantiate and destroy
vrc object pool exists exactly for that. you cannot and probably never will be able to instantiate networked objects, each of them must be present at the scene during the build
Cool, that makes sense. So just to clarify, the objects inside the pool still need their own sync scripts?
yes
Awesome, thank you!
is ownership gets transfered always exactly on deserialization
Can an object be owned by a player not in the instance? How does this affect synchronization, if at all?
Nope. That's illogical. Ownership will get transferred to the Master(or next master) when owner of an object left.
What makes it illogical? Is it because data are not saved in the world, and can only be transmitted by players?
Is it the case that a world must therefore revert to its default state if all players leave?
As an aside, is Networking.InstanceOwner not functional?
instance is destroyed when its empty
Instances actually stay alive for quite some time after the last person leaves. Some instances can stay up for up to 2 weeks since the last person left iirc
But a player always owns objects unless the instance is in such a state. When a player leaves, all objects they owned are returned to the master or the instance itself to be allocated to the next person who joins assuming the role of master.
But trying to think in terms of ownership while the instance, and by extension script execution, is paused is illogical
re: instances remaining active... I'd like to get some idea where that info came from because it sounds incorrect. VRChat worlds are Photon instances on Photon servers right? And they have server regions and scope. So what would the purpose be of having say an instance of a world on one regional server? Not saying it couldn't happen but I don't see why any attempt to keep the instance alive would benefit anyone.
An instance is not ephemeral which mean data of an instance doesn't get saved on server. Object being owned by anyone is illogical because otherwise everyone can own an object even there hasn't been in an instance.
Networking.InstanceOwner function doesn't exist. There is only Networking.IsInstanceOwner. But InstanceOwner doesn't indicate default object owner for networking purpose, other than a person who created an instance.
What is this then? A prank? 😂
Not even sure when this was a thing. But this function doesn't exist in the actual SDK.
A while ago, they announced something regarding people being able to make instances for events and links would remain active for a certain amount of time (2 weeks iirc), meaning that instance was reserved for that amount of time.
I'm also able to rejoin instances I've been in before through VRCX even if they have 0 players, but this may just be VRCX re-creating the instance. The first bit of info I know is true though
If network synced Objects retain positions and such and the instance stays in a half active state, I'm not sure or if it's just reservation of ID.
either way, I consider that staying active
I'll agree that "someone" posted something 🙂 I see no upside to an instance with nobody in it remaining active on a server for a significant amount of time.
My understanding is that persistence is in closed beta right now, so that is coming, but it's not something most of us have access to yet.
this confusion is because there are multiple places where an instance can be alive. In the API, it's true that they can live for a while - you can tell if you access an instance by the shortcode and it recalls all the other details like world and instance type. That's because the instance was still alive in the vrchat API. However, the synced data of objects within that instance are handled by the realtime networking servers, called photon. As far as photon is concerned, the instance and all it's data are gone the moment there are 0 people. You can rejoin the same instance a week later and it will have the same external properties (managed by the API) but all the synced data (managed by photon) will be lost.
As you might imagine, persistence works by making a bridge so that the data managed by photon can be stored more permanently on the API.
The fact that it's alive in the API is what I was observing and was drawing conclusions from. It is good to know that the runtime of the instance itself is ephemeral which was the assumption and I guess a very real observation seeing how games and the likes cannot pick up where you left off though this usually happens across instances and why I'm looking forward to persistence
The instance owner is the user who created the instance or the world author in the case of public instances. This is i formation handled by the API and not by Photon (see above)
Oh yeah is that accessible in U#? I’d love to try to make instance owner be in control of rules if possible, and just fall back to master
These are the sorts of discussions that I wish we could have in this group (from time-to-time) practical solutions to system-type issues (not always why isn't my variable changing). It is important that be part of a strategy but remember the instance owner may never be in the world. I define an "admin" that I tend to code against that gets set according to some conditions. Initial world setup falls to the master however as they are the first person in the world and they are the default owner.
I havent attempted it yet but I'm thinking a simple "If owner is instance master" check in the OnOwnershipAssigned of the CyanPlayerPooled object, which will enable an udonsynced variable on my UI manager, so when people click the buttons and it makes requests to my gameManager, the UI manager can check to see if the instance owner is in the world before setting the "ownership" flags for the master
And a callback that reverts ownership flags for the master when instance owner gets assigned*
^^
I also wish there was a more advanced discussions area beyond #development-advanced 
I think it needs to be a forum-type channel so a topic can be discussed without other replies getting into the mix.
Hi I'm running into a strange issue that I'm struggling to understand.
I basically have a script on a pickup that plays a random sound when the use button is pressed
However when the pickup object is changing owners (i.e. going from Player A -> Player B) the object briefly lags back to a previous position
This wouldn't be a problem but for some reason this causes Player A to hear the random sound 2 times
I'm unsure if this is an issue with my code, but I'll include it in case it is, any help would be appreciated
{
public Animator teddyAnimator;
public AudioSource teddyAudio;
public AudioClip teddySqueak1;
public AudioClip teddySqueak2;
public AudioClip tomAndJerryScream;
[UdonSynced, FieldChangeCallback(nameof(AudioRNG))] int audioRNG = 0;
public GameObject teddyBear;
public TextMeshProUGUI teddyBearRngDebug;
public override void OnPickupUseDown()
{
AudioRNG = Random.Range(1, 201);
RequestSerialization();
}
public void PlayTeddyBearAudio()
{
if (audioRNG < 100)
{
teddyAudio.clip = teddySqueak1;
}
else if (audioRNG >= 100 && audioRNG < 200)
{
teddyAudio.clip = teddySqueak2;
}
else
{
teddyAudio.clip = tomAndJerryScream;
}
teddyBearRngDebug.text = audioRNG.ToString();
teddyAudio.Play();
}
public int AudioRNG
{
set
{
audioRNG = value;
PlayTeddyBearAudio();
}
get
{
return AudioRNG;
}
}
}```
You're presented a very interesting example. One that is likely far more complex than seems to be warranted. One must always claim ownership of the gameobject before setting a property and calling RequestSerialization. I wouldn't have written it quite this way so determining why it plays twice may not be worth the effort. Consider this alternative. You're not expecting a late-joiner to hear the last sound right? But anything based upon sync'd variables will unless code exists to prevent it. So this is good example of where you could call SendCstuomNetworkEvent. No FieldChangeCallBack, no UdonSynced property and no RequestSerialization.
With the AudioClip properties set (you could use an array in place of 3 explicitly named properties) you don't need to set the AudioSource clip but could play them via teddyAudio.PlayOneShot(<clip>, volume).
You seemingly are trying for a random value in one of 3 ranges but You should be able to simply get a random value between 0 and 2 and using PlayOneShto reference the element of the AudioClip array (if you place them in an array instead).
See if that works for you.
You seem very well versed in udon code. May I dm?
I have a few issues with some scripts I'm working on
Hi Tom thanks for the message. I had originally tried to use the SendCustomNetworkEvent method but I was struggling with getting both players to play the same AudioClip. If the RNG code was only on the OnPickupUseDown method, it would only run for the player holding it, which makes sense. If I put the RNG code into a networked event, then both players would generate their own number and would often play different audio clips from eachother.
Ideally I only want the RNG to be run once, and then have that number synced to each player. But with this approach each player gets their own unique number.
Here is my updated code that has that problem but also has some of the improvements you suggested.
{
public AudioSource teddyAudio;
public AudioClip[] teddyClips;
public TextMeshProUGUI teddyBearRngDebug;
private int audioRNG;
public override void OnPickupUseDown()
{
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, "PlayTeddyBearAudio");
}
public void PlayTeddyBearAudio()
{
audioRNG = Random.Range(0, 3);
teddyAudio.PlayOneShot(teddyClips[audioRNG]);
teddyBearRngDebug.text = audioRNG.ToString();
}
}```
I have also tried to use the UdonSynced property on my audioRNG int, which at first seems to work, but after a couple of uses the number permanently is one sync behind on the second player's side.
can i dm you?
i have a few code snippets i need help with
Would you not be able to post them in this channel?
Have you set the turntable to the correct layer?
yes
ive double checked like fifty times
like in the editor, i test it, drop the disk on the turntable, and it works. i do a test build, it doesnt. i upload it, and it doesnt work there either
Are there any warnings or errors that might give us a clue?
nope
debug registers the collision
the script recognises the turntable
the disk just wont snap to point or spin
Layer 7 doesn't exist for vrchat.
thanks, i set it to layer 8 and now it works
need help with worlds i can no longer update any world and i get a error when trying to make test builds,
it only happen if i add a network sync if i remove the sync it works again,
but im doing synced objects so kinda need them to make my world tick
Have you tried using the Network ID Utility? You can find it under VRChat SDK > Utilities > Network ID Import and Export Utility
I think I was having an issue like this before with the Network ID's, I think I just made a fresh install of my project
Found a solution
if you get that error just go to the vrc network,
Then Clear the scene IDs
and do not regenerate new ones leave it empty, pres upload and you should be in the clear
I would suggest not trying to sync a value but rather have multiple PlayClip methods (so PlayClip1, PlayClip2...) for instance. So the caller sends the proper event and other players just call the one they were told to. You don't need to sync anything in this case. Also I suggest you use nameof(PlayClip1) rather than a static string as you have in your example.
I tried that earlier today and it was working perfectly 😄
It's not as 'programmatic' as I'd like but it works without any flaws
public void TeddySqueak1() => _audioSource.PlayOneShot(teddyClips[1], 0.7f);
public void TeddySqueak1Network() => SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TeddySqueak1));
public void TeddySqueak2() => _audioSource.PlayOneShot(_teddyClips[2], 0.7f);
public void TeddySqueak2Network() => SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TeddySqueak2));
Thank you for your help
According to VRC staff, that ends up not assigning network IDs to some stuff and will cause other problems. What you're supposed to do is use that utility, if it refuses to work, it's because you have duplicate names in the hierarchy (which isn't supported) and need to rename them to be unique).
If you ever change anything on an object that has network sync in future, you'll need to go back into it and click "Select" on all the objects that changed to reassign them... to themselves (no idea why that bit isn't automatic).
Hey everyone.
Hey. Anyone has any Idea? Since I Updated my Project to the latest SDK and the 22f1 Unity The VRC Network ID Utility stoped working for me. Whenever I try to import my IDs from my PC World to my Quest World it correctly shows the window with "Select All/Ignore All". After clicking one of those, it throws errors for each ID it tries to load in and shows me an Empty IDUtility Window.:
Obect reference not set to an instance of an object VRCNetworkIDUtility.<DetectConflicts>g__DoTypesMatch|29_5 (VRCNetworkIDUtility+NetworkObjectRef scene, VRCNetworkIDUtility+NetworkObjectRef loaded) (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:541) VRCNetworkIDUtility.DetectConflicts (System.Collections.Generic.Dictionary2[TKey,TValue] loadedRefs, System.Collections.Generic.List1[T] conflictList) (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:511) VRCNetworkIDUtility.OnGUI () (at ./Packages/com.vrchat.base/Editor/VRCSDK/Dependencies/VRChat/VRCNetworkIDUtility.cs:305) UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition) (at <80a8ce1980c648dca8e68f0d8aa3b930>:0) UnityEditor.DockArea.DrawView (UnityEngine.Rect dockAreaRect) (at <80a8ce1980c648dca8e68f0d8aa3b930>:0) UnityEditor.DockArea.OldOnGUI () (at <80a8ce1980c648dca8e68f0d8aa3b930>:0) etc.
Whats going on there? I allready tried reimporting the SDK. Same error. Can’t keep my PC and Quest Network IDs in Sync anymore.
I can’t even do them Manually anymore since update? Where is the ID Section gone? ^^
my "ducksUnlocked" variable isn't being updated for every player, i'm not sure why. this is my method i use to update it, and this method is only ran by a different script, which that one is only run by the owner of the object.
[UdonSynced] private int ducksUnlocked = 2;
public void TallyDucks()
{
Logger.ColorLog(this.name, "Tallying unlocked ducks...", "lightblue", false, true);
ducksUnlocked = 2;
foreach (var obj in foundObjs)
{
if (obj.activeSelf) ducksUnlocked += 1;
}
duckAmountText.text = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", ducksUnlocked + 1, totalDuckAmount);
RequestSerialization();
Logger.ColorLog(this.name, $"Unlocked ducks = {ducksUnlocked}, duckAmountText = {duckAmountText.text}", "white", true, true);
}
I'm pretty sure any synced variables need to be public
i don't think so, i have other variables in my project which are synced, but private
ah no you're right I'm thinking of methods 🤦♂️
yeah, plus, i already tried with it being [UdonSynced, HideInInspector] public and it still didn't work
have you tried doing " Networking.SetOwner(Networking.LocalPlayer, gameObject);
"
Before requesting serialization?
well, i have a different script that runs TallyDucks(), and that script is only ran by the owner of the object
so i figured TallyDucks() would only be getting ran by the same owner
I think it's on a per-script ownership basis, I might be wrong but it's worth a shot if you're willing to try it
It's been a few months since I did any big networking things so I might be totally wrong
yeah i'll try setting the owner of the TallyDucks() script as the same owner of the other one
I hope that works or that's the third thing I've said to you that's wrong in a row
it probably is per-script/per-object
well unfortunately it doesn't update for everyone still

actually it's probably cause i don't have any OnDeserialization method set-up
i don't really know what to do with that though
actually
nevermind i'll just try and set one up
i think this is all i need
public void TallyDucks()
{
Logger.ColorLog(this.name, "Tallying unlocked ducks...", "lightblue", false, true);
VRCPlayerApi spawnerOwner = Networking.GetOwner(duckSpawner);
Networking.SetOwner(spawnerOwner, this.gameObject);
ducksUnlocked = 2;
foreach (var obj in foundObjs)
{
if (obj.activeSelf) ducksUnlocked += 1;
}
duckAmountText.text = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", ducksUnlocked + 1, totalDuckAmount);
RequestSerialization();
Logger.ColorLog(this.name, $"Unlocked ducks = {ducksUnlocked}, duckAmountText = {duckAmountText.text}", "white", true, true);
}
public override void OnDeserialization()
{
duckAmountText.text = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", ducksUnlocked + 1, totalDuckAmount);
}
I'm no compiler but it looks good to me
btw, one thing that might help you is the NetworkedEventCaller package, it allows you to call custom network functions and also pass parameters through, unlike normally where you can only call a function. https://github.com/Miner28/NetworkedEventCaller
I've used it myself and it's really easy to set up
would it sync stuff for late-joiners too?
I don't think it does, it's just for calling functions with parameters to save having to sync the parameters and then call the function on deserialize
yeah i'll probably keep syncing stuff manually then. i always have stuff synced for latejoiners
i love having synced stuff in worlds, its just hard to make sometimes
understandable yeah
this is my full script currently, it still doesn't update the ducksUnlocked (for non-owners)
If you put "Debug.Log(ducksUnlocked)" does it have the right value?
in OnDeserialization*
i don’t think it would runOnDeserialization when testing in unity, cause it only runs for remote clients
I thought you were testing with 2 clients, otherwise how would you know the script doesn't update the ducksUnlocked?
well yeah but i don’t have the console in-game
you can turn it on by doing right shift + ` + 3
oh you also need to put --enable-debug-gui in your launch settings
looks like OnDeserialization is never run?
i never get the log on the second client
so, on the object owner's side i get all these logs, but on the other client i don't get the "SetText, Unlocked Ducks = X" at all
it just seems like RequestSerialization isn't working, or OnDeserialization isn't working
cause it seems that it never gets run
Did you try to Debug.Log and check to see if OnDeserialization is actually running or some issue with logging it.
I've encountered an issue like this before. I am unsure how I resolved it but another thing did you set the Sync Mode to manual?
i did not, i forgot you have do that
what exactly is the critera for using manual sync mode? is it whenever you use Request or OnDeserialization?
Pretty much, also a note from the docs "Manual synchronization is intended for data that changes infrequently and where intermediary values matter; like the positions of pieces on a chess board. Users should not expect high speed updates with manual serialization."
i wanted to make a script where instead of having 1 thing turn on/off when holding an object and pressing it like a flashlight it will have mutiple objects turn on/off. I combined 2 scripts but it turns on the objects when I have the main object in my hands. How do I fix this?
you want a script that simply enables and disables an array on objects when you press trigger, yeah?
yep!
cause I have a script where it does that but for 1 object
Interact is the wrong event to use then. Interact is when you point at an object, an outline appears, and you click it
also, setting isActive seems to do nothing in that graph, because you don't use the isActive bool for anything
otherwise, simply plug in the OnPickupUseDown into the For node, and it should work as intended
set the sync mode to Manual and it still doesn't work. what i did to test was put a log at the top of OnDeserialization, and i never saw it show up for the non-owner. i currently have a log in the SetText method, and i also don't see that one show up for the non-owner aswell
every single log shows up, and all the variables update correctly for the object owner however
it's just remote players who don't see the changes, update the text, or recieve the logs
it doesnt seem to be working :<
is your pickup set to Autohold: Yes
You tried Debug.Log by itself too. It looks like your using Varneon logger.
i’m not actually, i made my own static class cause i like to make logs a specific way
all it does too, is take some variables and put them into my specific format in a Debug.Log, so i know it works fine
cause i also use it for everything else, and i see my custom logs on the non-owner client from other scripts
Does the game object with the script get disabled at any point?
yes!
it does actually yes. it’s part of my UI so it gets disabled when the menu is swapped. should i put it on a separate object so that it never gets disabled?
hmm. show the graph again in its current state so i can check it out
you aren’t getting the length of anything, and you aren’t getting the GameObject out of anything. you need to plug your Targets variable into the Get Length and GameObject Get
using a normal gameoject it wont let me
well you want to toggle an array of objects right? multiple objects?
yeah, that’s an array. a container for multiple objects, as many as you want
oh!
and make sure it’s a public variable, so you can add objects to it from the inspector
made it public!
i put that in get length
if Im wrong please correct me!
now just test and it should work. as long as you add things to the array from the inspector
thank you so much!!!
do you also want this to be synced between players? if not, this probably should’ve been in #udon-general
no! just for players to have it for themselves without it syncing!
it works!
alright
That most likely why its not sending any information. When you disable a gameobject with a synced script it cuts of the sync and receive sync.
It should be on a gameobject that is not disabled.
this was my issue. thanks!
now people can see how many ducks have been unlocked :)
:3
I had this issue in the past and that was my issue lol.
networking is so annoying sometimes, but networked mechanics are always better
how is this not synced? i copied my other script which is synced, but just removed some stuff i don't need for this other project
i don't know if i removed too much or what, but it's basically the same, yet the ducks don't spawn for both clients when i test
Do you confirm that SpawnDuck get called?
for the instance master/object owner, yes. i see the logs and i see it spawn
it’s only the non-owner that sees nothing
You'd have to be aware of object pool ownership. And if the spawn object has object sync, you will have to set its owner to the pool owner before you change its position.
well the object pool owner should be the same one as the script object’s owner while i test, because it’s the only player in the instance before i click go on the second client
and i would assume each object from the pool is owned by the same client aswell, because i believe ownership of objects automatically goes to the ayer who has been in the instance the longest
So does is work all correct for a single player?
it does
Do the objects have object sync component?
yes, they all do
Does master see the object spawned in and position set, but other players don't see object spawned?
yes, the master sees it spawn, and in-position. other players don’t see anything happen at all
I have a suggestion you should cache player if you it more than once.
private VRCPlayerAPI player;
void Start()
{
player = Networking.LocalPlayer;
}
Is the GameObject with the pool disabled?
Your code seems to be fine. Although, can you show me your VRCObjectPool setup?
nope, nothing is disabled or gets disabled, besides a separate empty that contains the object pool objects (not the one with the component), so i can disable them when the player is far away
i can show it later
i mean, i’m putting this system into a pre-made world, so could there possibly be some other function or something from a different system that would affect mine?
i don’t know how it would if it’s all separate objects in the hierarchy though
Im not to sure. I can't really give a full view until I see what your code is doing.
DucksContainer is what gets disabled when the player is out of the area
It could be that maybe. I mean im not 100% sure but your code looks fine. The object state should be syncing meaning its something happening outside of the code.
guess i'll try disabling my cull trigger
nope, still broken. i don't know what's wrong about this
if it transfers ownership to the player who it wasn't working for, it then works for them, but it gues that just makes sense
but i really don't understand why it won't spawn the ducks for everyone
i just noticed something
when it tries to spawn a duck, for the client that isn't the owner, it shows for a split second but then disappears
so something is disabling it again maybe?
but i don't know what would possibly do that, cause i haven't made anything that disables then besides a button that has to be pressed
it may have been my respawn triggers, which call Respawn on them if they enter the trigger area, so maybe when they spawned they somehow hit it and go somewhere else, but only for the non-owner??
i don't know but i changed how it works and i'll test
nope, that wasn't the issue
but at least i see what's happening now
this is what it looks like for the non-owner player when a duck spawns
they all appear in-place for a half-second, and then disable or go invisible again
(for the owner, they see all the ducks where they should be all the time)
@strange crane does this clip help with diagnosing my issue at all?
Could you VC?
I can't really see what is happening in unity and whats triggering.
yeah just a moment, i'm gonna look at a different system for a minute
Alright! Just DM me in a min if you still need help
Before i make mistakes, short/int16/uint16 are properly working with networking and will result in halving the amount of data on continuous sync?
they do, i mostly only use ushort or byte and it improves bandwidth usage a lot (compared to i.e. int), though im using manual sync, just keep in mind vrchat adds some additional uncontrollable bandwidth, so the number in the debug menu may be higher than 2 bytes
oh byte is even better ye thanks
be aware that byte is unsigned by default. If you want a signed byte you need to use sbyte (byte is the only numeric type where unsigned is the default)
ye, i know. also udon doesnt allow to set -1 as a default value for short. Also this, every time you copy these two, they mix up nodes😡
does OnOwnershipTransferred ever get called?
transfering ownership of the handler to whoever it will work with, console says ownership transfer occured, but OnOwnershipTransferred has never been called
Works fine for me, make sure the object has manual or continous syncing
manual, spawning object out of the pool and assigning it to the player right away

