#udon-networking
1 messages · Page 19 of 1
That depends on a lot of factors.
For example a vr player holding a pickup with continuous sync will bump its rate up to like 10 per second afaik.
I hadn't consider that. Could we split the workload between users to better effect?
you diff can but then you need to keep track of who does what etc
the Hz thing sesems to not really matter lol. even when you do a few syncs per so often at 20-30 bytes it goes to 0 lol
Hmm, if we design the game in such a way that there is about 40 enemies per player, each player is sending 10 vectors a second.
We could have 4 vs 160 with each individual enemy getting updated once every 4 seconds and simulated between.
That's definitely feel like a epic battle.
Working out how to efficiently simulate all of those would be a separate also hard issue
it is not practical for vrchat to sync hundreds of enemies individually as you quickly run into bandwidth limit, but you can however design the game to be much more deteministic (e.g. spawn enemies waves being a single event and have enemies behave with a predictable pattern), or, make it less important to have two players see the same thing (e.g. make enemy count so massive it does not matter if individual enemies are in the same place or even with the same health count). Like vacuum cleaner simulator which is largely a single player experience, and the "co-op" happens at a more macroscopic scale rather than individual particle enemies.
that is an awful lot of data to send per person. plus the overhead. and one person would have to be the one that tells other people what they are in control over etc
not to mention you have to set ownership of them all which also requires abit of wait
It's 160bytes (including a health value) plus overheads. Well within viable realms.
For this to work we have a AI controller per person. So they have ownership of that one controller.
What you can do is combine Vacuum Cleaner Simulator, with individual boss monsters that you need multiple people to kill, that will give you the full experience
To be clear, full prediction and only sync the spawn is my fav tactic. But I'm trying to work out ways to push the envelope.
eh. Udon is just not made for it. it can be done but the real problem comes to when you need to detect who hits what etc
and you then need to decide on how things sync
Where's your sense of adventure
Once location is taken care of we just need a handler that sends what the player has hit. Depending on the design of the weapons this might be a lot of things... But typically not that many.
Given that the list of players is small it won't be much of a issue for each AI controller to check against each player handler if any of it's pawns just got hit.
sense of adventure? oh i am on an adventure all the time. i just know the limits of Udon cause i tried various things and i always hit the same limit. Throughput aka how often you can update
and to you give to an idea. of it i have a world with roughly 50-64 people using it now and then. at 64 people it runs into a wall for data output and it slows down sync a crap ton.
even through in my world every person only syncs 20 bytes every 30 + seconds.
and if u take the total amount of request being done its only at worst 128 request per 30 seconds. but that's a unreasonable amount since they can only take an action if u will every 30 seconds
and not everyone does it at the same time
I mean, that's a separate issue. We players send a lot of data. I wouldn't run a game with a hundred enemies without limiting it to like 4 players
What player hit can also be locally determined (not precisely) using where the player is looking at. But remote player look direction is very sluggish and only updates about every half a second. But you can design the game around it.
Yeah that becomes a issue of "what sort of lag would you like"
Lots of options.
None perfect
And it's quite delayed.
well i can only tell you so much and from my experience udon just cannot handle a ton of requests often
best call is to try and see how udon fairs if u have a constant Manual going off with say 100 bytes in each.. and have 4 people join you
One big request rarely is preferred over smaller requests more often.
This is because you incur the header cost on each request.
well
not really possible when all the data you have is small 😄
build in delays and other things so people cant repeat the same actions for gameplay reasons
buuut yea. that 12 byte overhead for just having something sync is on the higher end
@grave sentinel's VRChat Data Sizes Calculator
https://docs.google.com/spreadsheets/d/1WXrHxJQW_evivVpajzLnrZnmu0otWxVsBsfUZiQXLFg/edit?usp=drivesdk
2880 bytes per 30 seconds at worst in my world. at 80 people. but eh. it diff is ALOT higher then that. with 50 people i would often sit around 10-11 kb /s without doing anything. that said thats with face tracking and fbt and talking.
without that it would sit around 8 kb/s again 50 some players.
which i still find odd that the amount goes up so much with each player joining
and that is Data Going Out from my side.
which also concerns me because we never really got a clear answer as to if more players in world increases data out per person. because that seems to be what is going on.
Can this be pinned? This is absurdly helpful
- VRChat network specs page says udon scripts can send 11 kB/s (KB/s?), is this per script or per object or per client?
- The Time Estimation tab on the sheet, is it a duration of sending, or duration waiting before send, or a roundtrip time? Is the time in seconds?
- What happens if network is clogged, is the queue still going to send? Or is the queue abandoned? (I assume you cannot add to the queue anymore, but what happens to the stuff already in the queue?)
- I assume manual sync means only sending once, what if I update the variable before it is serialized, does the old data get overwritten, or will it add to data size?
- Continuous sync how often does it happen? Does it only send data when variable is changed, or is it always sending at a certain frequency?
- Is continuous sync "limited to roughly 200 bytes per serialization" per object, or per script? or per client?
Or per variable?
(If my player stands still, therefore no new vector3 player position, does it "save bandwidth" for other scripts?)
Thanks
- In the estimator, on the Time Estimation tab, why is there five different times for 64 Byte? (~0.11 to ~0.18 s?)... and why is it going up and down?
what's the motivation for reusing a single VRCImageDownloader to download multiple VRCImageDownloads, rather than just making one downloader for each?
- outbound 11kB/S is per-client. Also that number is very rough and depends on a lot of factors, in most cases it's "up to" that speed but realistically lower
- Time estimation is duration after sending, there is no roundtrip. I don't know the exact measurement used for that though
- If network is clogged, variable sync will never be thrown away. It'll take some time to work through large messages, but small messages can slip through without too much delay.
- When you requestserialization, it takes a fraction of a second before the network tick comes around and packages up your data. If you make changes before that happens, they will be reflected in the sent variables. When this happens, you'll get the OnPreserialization event which is the last chance you have to make tweaks before sending. After that, you'll get OnPostSerialization which tells you information about how it went.
- Continuous sync repeats whether or not the data has changed. Under normal conditions, it's about 5 times per second, but while clogged will slow down.
- continuous sync is limited to roughly 200 bytes per serialization, in the same way that manual sync is limited to roughly 280 kilobytes. That is per gameobject, which can include multiple scripts
So Im starting to learn how Object pools work, I assumed a setup like this would work but it causes a null reference exception.
The context is that the udon is part of a spider which when triggered, will "spawn" a web trap that lies on the floor for the player to avoid/shoot so the idea is to On command, take a web trap asset out of an object pool and the resulting game object is to copy the location of "SELFgameobject" (The spider) Can I get some guidance on this?
maybe your pool doesn't have available object, so it is null
To be fair I only put in the first 10 of 100 objects I plan to have in there but I assumed it would attempt to grab object 0 first then 1, onwards?
if you attempt to TryToSpawn, but there isn't an object available, then the function will return null
so you'll need to add a check for null there
So every slot has to be filled in? uhhh what a chore :L
I just wanted to test before commiting lol
ah if only there was some sort of way to add many objects at once into an array....
sadly you cannot add multiple objects at once into an array of a graph script, only works in U#
you can lower the pool size if you really wanted to test first
Yeah just done that, Imma test now
Ah it works as intended
So I assume the object pool will always attempt to grab the highest item in the list?
if it for example was returned?
not sure, based on the docs the order does not seem to be guaranteed
maybe only if you're spawning, but if you also return things to the pool it might change the order that it spawns objects in
i guess it grabs the first object that is inactive
Im testing now, it grabs the next one in line, regardless if an earlier object in the pool was returned
wondering what happens when it reaches end of the line, lets see...
Ah, it cycles
goes back to the top once it gets to the botto,m
makes sense
Welp, from everything Ive experimented and learned, I think this game might become a reality, I just hope people will actually play it :L
interesting, why would it cycle through the list, rather than starting from the top?
any one know how use VRC Enable persistence i cant fined any videos or AI help
there aren't hardly any videos out on it, and AI certainly won't help you at all.
Your best bet is to read the documentation, or take a look at the examples in Example Central
https://creators.vrchat.com/worlds/udon/persistence/
What is Persistence?
@twin portal but is is always enbled or i need turn it on some how ?
you'd need to write a script that would use persistence, or use a PlayerObject with persistence enabled
so AI wrong then
- Setting up Persistence:
In your Unity project, go to VRChat SDK -> Show Control Panel -> Builder.
Select your world scene.
In the VRC World component (on your world descriptor GameObject), find the Player Persistence section.
Check the Enabled box. This grants your world permission to use the persistence API.
looks like the did not train AI much on Vrchat systems
@twin portal good to know thanks
its 200 per client
Is it possible to make it so that the more players that join the world the more cars will spawn into this parking lot? I'm trying to make it seem like the players are visiting this business in a more immersive way. and if it is possible, how would I do this?
I am currently using a component which has this ability.
It spawns a prefab each time a player joins.
But I don't know right now how you can put each object in the right place, but I believe it's possible.
maybe instead of spawning an object, it instead toggles objects on and off?
PlayerObject
It was called.
I dont know.
I only know about my way.
Do you think that difference matters?
well, it'd have them placed in the correct spots and orientation
You could use OnPlayerJoined, then read what the current player count is by using GetPlayerCount. Then you would just enable or disable the objects depending on how many players there are
VRC PlayerObject automatically spawns (a copy) when someone joins, it's meant for situations like this, when you want to give out copies of an object to each player
You can simply have an onEnable-->move to an unused parking space
- You have have an empty gameobject to be a ParkingSpaceManager for managing parking spaces, and have the PlayerObject talk to it.
- Setup variables:
- In your PlayerCar, make a variable:
parkingSpot[](Transform array, public, where you store a list of empty gameobjects for each parking spot) - And then make a variable in ParkingSpaceManager (synced):
parkingSpotOccupiedBy[](playerAPI array, default null, when someone takes the spot, store their playerAPI)
- In your PlayerCar, make a variable:
- When a car spawns, have it choose a spot and ask the ParkingSpaceManager if
parkingSpotOccupiedBy[123]is null (not taken), if so, the car can move to theparkingSpot[123] - When someone leaves, their spot in
parkingSpotOccupiedBy[]should become null (idk if it does this automatically, you can test) and becomes available for a new car
- When a car wants a spot from ParkingSpaceManager, you set local player to be owner ParkingSpaceManager, update the
parkingSpotOccupiedBy[123]to be local player, then request serialization to sync it to other people and late joiners- Do this with a custom event inside ParkingSpaceManager, call it TakeASpot or something, make it do the spot choosing logic, record a non-synced integer variable called
localPlayerSpotIndex, and update and sync as mentioned - PlayerCar can then read the integer from
localPlayerSpotIndexand set transform toparkingSpot[12345]
- Do this with a custom event inside ParkingSpaceManager, call it TakeASpot or something, make it do the spot choosing logic, record a non-synced integer variable called
After I read this guy's suggestion I think this is the best solution for your scenario.
Gives you a lot of control over what the full parking lot looks like.
Yeah PlayerObjects overcomplicate it if the ONLY goal is for a car to show.
If you wanna add anything ontop of it like...
- Custom license plates
- Custom cars/colors
PlayerObjects would work better
I suggested that method over PlayerObjects bc you've already got all of the cars placed nicely... PlayerObjects always spawn the new object at the exact spot the original is, so your script would have to also manage placing the car. In most ways you'd kinda have to redo the work of placing all the cars, so simply hiding/unhiding lets you use the work you've already done
Even if you wanted custom stuff for the cars, it'd be easier to save data into PlayerData instead, and simply load the info onto the already-placed cars
Oh yeah if it is only "show more cars based on players count" then you don't need PlayerObject, it can be done locally turning objects on and off as suggested, no networking required.
I assumed it is drivable cars each player can drive away/customize hence the PlayerObject suggestion, but you don't need it if it is just background decoration.
Could I have a visual demonstration..? I'm a visual learner (I haven't touched upon scripting in my life. I usually just use CyanTrigger)
what part of that are you stuck on?
Add a script to your parent that holds all the cars
and have that script toggle X cars on when a user joins
the part where I have to use "GetPlayerCount." I can't find that in the Udon graph
like I said, I have never touched the graph
I always use CyanTrigger as that makes things far less complicated
believe it's in PlayerApi > GetPlayerCount
You can use Udon to retrieve information about players in your world instance.
yeah, I got no idea what I'm doing
before doing anything, what do you want to do?
like in detail, what is logical steps you are doing
describe in plain english
what effect you are trying to achieve exactly
Whats the best way to destroy an object if a player leaves?
And does "OnDestroy" actually work?
this is in my instantiated object. PlayerOwner is stored at the start when it's instantiated.
u better just set it to inactive rather than destroy, your world can have millions of people coming and going and there aint gonna be enough objects to destroy
If the object is networked it shouldnt be destroyed (as it is pooled) and should just be returned to the pool
If however it is locally instantiated then you can just use GameObject.Destroy
this is for a locally instantiated object to add a random color. And a color sphere above the head of players. Then it uses the stored color to paint the color of any NPC that is owned by that player. Im trying to make a debug mode so, at a quick glance, I can see how my ownership system is handeling things.
Then you can just destroy the root gameobject with GameObject.Destroy and it will automatically destroy all children as well
Networking.Destroy is an internal API as far as I know and should not be used
I do not see destroy under gameobject
Ahh my bad its under UnityEngine.Object.Destroy
found it. thanks
all the car objects are disabled by default. upon a new player joining, one additional car enables. total number of cars is 25 for all parking spots. once the player leaves, the car that enabled upon their join gets disabled. I wanna make it so that the players "drove" to the location for a slightly more immersive feel
"drove"? u mean actual driving mechanics?
or just spawning next to a car
Since you want cars to be assigned to player, probably PlayerObject is easiest
So all you need is a script on the car to position itself once spawned, with a parking spot manager like mentioned before
probably want something that works a little like this yeah?
this was the simplest way I could think of doing it
every time someone joins or leaves, loop through an array of the objects you want to enable or disable. Whether or not the object is enabled or disabled is determined by player count
If I want to make a "network clogger" button for testing purposes. What would i do?
I want to purposely clog my network.
And can I have 1 client try to send too much data.
thanks!
Sending a bundle of 10k network events will be a nice consistent clog for a couple minutes
@frozen igloo uh.... is there a "make garbage data" node?
or can i just send a massive array with random numbers in it?
We've already got that, it's SendCustomNetworkEvent
dont try to use a for loop to send all of them on the same tick. Your client may hit the 10 second max tick time then crash the script
It's not recommended to destroy objects. It's better to just deactivate the object. and assign ownership to a new player
not synced variables, individual events - just call SendCustomNetworkedEvent 10k times, using a for loop
send it to some event that does nothing
Having a slight issue working with the object pool system, trying to spawn some enemies that ive made using a debug tool, it works great but after the first frame of them spawning at the desired spawn location (Udon pictured) they teleport to somewhere else that isnt intended, very weird
Seems the issue lies with the nav agent itself, it was teleporting itself to a part of the map which it deemed compatable so now forcing it to spawn where I want it to be causes it to error out even though there is a nav mesh for it to use...
Lol, managed to fix it myself by simply telling the nav mesh agent to warp to its own current position 😛
is your spawnposition not on the nav mesh
It is but it was treating it as invalid
I basically just told it to teleport, then when it checks its on a navmesh which it fails, it then tells itself to just teleport to itself again but using AI nav mesh and then it works
is it close to the edge or something
ok... so i may be dumb here... but if a player joins an instance with people already in the instance. Do that player get an OnPlayerJoined for everyone already in the instance?
so like... if someone joins a lobby full of 40 people. They get 40 OnPlayerJoined events plus their own onplayer joined making it 41?
yes
When you join an instance, you execute OnPlayerJoined for every player in the instance, including yourself. When another player joins your instance, you only execute OnPlayerJoined for the player who joined.
it's why some worlds spam you with those stupid notices about joins when you join
You can stop that from happening w/ playerID. If < local players playerid, retur
At least I would imagine that's how you'd fix those spamming moment
what is the value returned by GetPlayerId()? is it universally tied to their account, or just an integer for the current room?
do all the users in the room see a given player as having the same id?
yes
and id 1 owns all the scripts/objects by default, doesnt matter if hes instance master or anything
oh interesting....
but what if that player leaves
I assume the IDs don't change, so I guess it's just whoever has the lowest id?
then other player gets all his objects. but not always next one, questies/high ping people can be skipped in populated instance. so just dont try to predict it
is it always starting from 1 and goes 2 3... ?
yes
Does that mean there's no way to get a permanant reference to a particular player? Just store displayNme and hope for the best?
pretty much
we don't have access to their account ID or anything
though players don't change name that often
Gonna have to change my racing records format
I have seen a strat to generate an ID yourself, then save that persistently
persistent data is another way to "save" a player
I'm trying out sort of a client consolidated records things: each player saves race records when they're in the instance, and when they join or have someone else join, the records are combined and only the top 10 are retained
Which means you could be racing vs a player you don't know
I thought IDs were like, global IDs, not instance, so I just have to change to saving the displayName. That personalID idea is neat though
nop. IDs are great at referencing specific players within one instance, but are only unique to that instance
is there any way to detect whether someone has untrusted URLs enabled?
I guess just firing off a request to an untrusted URL and seeing if it goes through, and hoping the problem doesn't happen to be on the server's end
I think that's the best idea, the video player will return an error but I don't think there is one specifically for it being blocked due to being untrusted
There is a working example in Japan Street, random number generator top10 leaderboard, so the concept definitely works
will vrchat persistence work with sync set to none
yes
If you mean per-variable interpolation mode, then none just means no interpolation, so it will work perfectly fine.
If you mean per-object sync mode, then it depends which type of persistence you're asking about.
PlayerObjects save data through networking associated with the object, so setting sync mode to none on a playerobject will disable persistence for that object as well.
However, playerdata is different - it's handled by an independent set of objects which are always available, and any script can interact with them. So setting sync mode none on some random script doesn't prevent playerdata from working, nor does it prevent that random script from interacting with playerdata
So if I understand this correctly.
You can psuedo sync via playerdata...
Like it isn't ACTUALLY synced, it is just pulled and then repulls data whenever the PlayerData is updated (as long as you have it setup correctly)
nothing pseudo about it, playerdata is standard syncing. You can think of them as a pre-made playerobject with a dictionary and a nice interface
the syncing does not go through the object which is interacting with playerdata, playerdata does all its own syncing
Yeah okok
PlayData -> Saved data that auto syncs without needing to tell everyone to request serialization
instead, you just do the dataupdated thingy and check if the player is the owner of the PlayerObject to update said variables
yeah, it has it's own internal requestserialization that it takes care of automatically so you don't need to do it, but it still definitely operates off the same principles
Yeah ok. Cool. Good to know 🙂
what is the order of events for a non-owner loading into a room and receiving synced udon variables? does the udon script not start executing until the synced values have already been received, or is there an event that I need to wait for before ever looking at a synced value for the first time (as a non-owner)?
I guess you just wait for the first OnDeserialization?
it's what I'd do
order of events may change in a future update, even if by accident
yes, OnDeserialization is where you'd fire anything that needs variables to be synced
all other events can very likely run first (scripts start executing as soon as they are enabled, they don't wait for network sync)
Hi.
I want to synchronize player objects.
I want them all to copy the position, rotation and velocity from the owner's version.
How do I do this?
The VRC Object Sync component synchronizes the transform of a GameObject with all players in the instance. It synchronizes the object's:
slap on a VRC Object Sync, it does it automatically
Nice.
Will that not interfere with my code that directly alters the velocity?
Or do I need to make some adjustments?
it doesnt alter your velocity, it just tells other people what your velocity is
no adjustments
Okay.
I'll try it
Hello everyone!
Sorry if this question has already been asked a thousand times. I’ve been searching but couldn’t find the right keywords to get to a proper solution.
I’m working on a script for catching eggs in a VRChat world. The idea is to make it competitive and fully synchronized in real-time, so players are racing to collect the most eggs.
However, I’m running into a race condition when two players grab the same egg at the same time, both end up getting the points. To fix this, I modified the script so that points are only awarded after the player gains full ownership and deserialization is complete.
The issue now is that the egg doesn't get caught immediately after the ownership transfer. After some debugging, I found a strange behavior in Udon: when a user gains ownership of an object, the OnOwnershipTransferred event triggers for everyone except the new owner after the ownership has changed. But for the owner themselves, the event fires before they actually gain ownership, so the race condition still happens locally.
I’m not sure why it works like this, or how best to handle this properly. Has anyone found a reliable pattern or workaround for this kind of issue?
Thanks in advance for any help!
OnOwnerShipTransferred triggers for ALL including the one getting it. you need to check if the user is the new owner by simply checking in that Method if the Local user is owner of the object
so in that case my user never get owner of the object...
i did a simple:
public override void OnOwnershipTransferred(VRCPlayerApi player)
{
if (Networking.IsOwner(gameobject))
{
Debug.Log("is Owner");
}
}
the log is never triggered when i do a setOwnership. it sometimes get triggered on the old owner but never on the new
it match that picture i've seen on the vrchat wiki where it clearly show that th event is fired before the ownership is fully transfered. but right before,
so another ownership request can be processing in the same time...
i know i can do :
public override void OnOwnershipTransferred(VRCPlayerApi player)
{
if (player == Networking.LocalPlayer)
{
but it won't fix my issue that waym because he still not is trully the owner
that chart is more for how the logic works to negotiate an ownership transfer (if needed, as by default there is no negotiation), and not so much for how to deal with simultaneous ownership requests
which is a difficult problem to have a solution for
Maybe have them record the time of pickup using server time, and compare the two in events of double pickup
And if they happen to be at exactly the same server time (very unlikely) then, compare their latency (idk how) and pick the one with longer latency (the logic being, if playerA has lag and still click at the same server time as playerB, playerA probably clicked first in real time)
And if still the same, randomly pick one
i did thought about that but didn't know how to get the server time but if it's possible it will solve my issue, thx !
the issue is that i want to resolve that precise case... one of them get a refusal bu after that try again with the serialization and manage to send it... i don't really know how it work but with my debug i clearly see that each one send their version of the data,
forgot to hit enter but was also going to second the idea of getting the server time and comparing, I believe you get it through Networking.GetServerTimeInSeconds() or GetServerTimeInMilliseconds()
but I'm not really sure how you'd compare one player's time to another
maybe something crazy even like a DataList, that stores the playerID, egg number, and timestamp they collected that egg?
then you just search through the list, and whoever has the lowest timestamp for a particular egg, then they're the one that gets the score for it
might get kinda too big to sync
oh I remember now my plan B, have a central script that only ever has one owner, then have clients send an event to the Owner whenever they collect an egg
whoever the Owner receives first, they get the credit
PlayerObjects could be used to store the timestamps for each player, then the Owner can loop through all PlayerObjects and check their values
there are many ways to cook an egg....
I have a general question, if you guys don't mind. I have a prefab game object that is a pickup item and has object sync script. I also have a script that generates the prefab game objects. Would the generated objects be synced with everybody? Is there a special way on how to do this?
depends on how they are generated. If they are being instantiated, then ObjectSync will not function for the objects
if you have an ObjectPool of the objects and spawn them from the pool, then they can be networked
Yes, what I am working on now, the game objects are initialized when both players are ready and one of them would press start. The game object owner generates a random seed and then send that to all the other clients. Then the game clients generates the game objects with the see. But from what you said just now it sounds like that won't work.
simply the best way to do it is to have the current owner. which most likely will be the that is master atm. to A either cache all incoming requests and then check which one should be given ownership by the time of arrival. Or B use first come first serve. And simply lock the object down so no one is can take ownership.
that is to say this is determined by the current owner and only allows one to go through
and then once the new owner takes well ownership they do the same if another person needs to. take ownership
if your script exists in the editor (and is not created at runtime) OR is part of a PlayerObject, you can instantiate child objects with your synced seed and it will show properly
however any instantiated object cannot be synced (including scripts), unless a another script takes care of that (as mentioned above)
Is there a limit as to how many items in an array I can use? Getting the feeling this being caused by large arrays.
believe you've just gone out of bounds of the array
arrrays have no inherent limit, but the total data size per gameobject cannot exceed 282KB
Oppsy. I had the script set to Automatic 🙃
No wonder I as out of bounds
is there a timing issue with instantiated udon behaviours?
do i need to wait a frame?
you can run functions on them before they've had their own start, so if you're relying on that to get components and stuff it might break. But there's nothing stopping them from running code immediately after, so what I usually do is implement an "Initialize" function which if needed, runs at the beginning of every function received from an external source
So.. like a custom event in them? @frozen igloo cause this is an issue that is popping up recently.
I'm just spitballing what it might be based on what you said - if that doesn't cover it then you're gonna have to go into more detail on what you're experiencing
Does Awake run during the same frame an object is instantiated? Maybe that would work?
Reading through this to try and guess
Udon doesn't have Awake, unfortunately. It's definitely on our radar though
Oh...didn't know that wasn't available. Guess I never tested it in-game
I think this paragraph implies that you could have issues depending on when an object is instantiated
Wait, so there is a chance? 🥹
🤷♂️ idk.. problem fixed itself?
Tested last night. No yeeting to the error world?
Does anyone happen to know if this network debugger tool is reliable/accurate? Cause i notice for some thing like object sync it doesnt show any data going in an out for it.
is it safe to say that Ownership is handled by VRchat realtime server but calls to Ownership is client side?
like... does VRChat realtime server prevent two clients owning the same thing?
or is that a situation I need to be aware of in scaling?
Phased might know more or be able to explain better, but from what I remember, ownership is technically handled client side. The info on who is owner of what is sent alongside synced network data, essentially going "this data you just got, this player is owner of it now, so use their data instead of what you currently have"
This can cause issues if two clients request ownership simultaneously; which one becomes owner, and who's data is used?
I haven't done extensive testing on it, but both client's ownership change and their data will eventually be received by the other players, but the order isn't guaranteed, so as one object fights for ownership, it could create unexpected states in the data, depending on the implementation
The server maintains the source of truth of who the owner is, but when you transfer ownership the client will briefly override that while the message is being sent to the server. In most cases, the server then comes back and either confirms the transfer or says "nah it's actually owned by somebody else" but in practice there are rare cases where that can break, particularly when a bunch of people are trying to take ownership at the same time. That said, if it's just as simple as 2 people at once usually that doesn't trigger it. In my testing I've only seen it with 4+ people
Ok in my NPC system ownership transfers happen after serialization where current owner is like "I'm transferring ownership to ID n"
And player goes "I'm ID n set ownership to me!"
Everyone else is waiting for the next Serialization after OnOwnershipTransfer (or whatever that event is called) for continuing logic.
Yes, its kinda redundant and there is a slight pause in noc behaviour but it works REAALY well when animating NPC issues dynamic group behaviour.
After that checking ownership of the ownership module is the only gatekeeping from split brain syndrome.
Trying to work from OnDeserialization to try and learn other ways of networking, but is there a way for me to check the old value of a variable to see if I even want to run code based on its state? I usually use properties to trigger the changes I want, which automatically only run if there's a change to that variable, but here I don't have that
wait a dang second, actually I think I don't need this and I'm confusing myself
@strange token I have a "byte" counter in my code that just counts from 1-223 then back to 1.
If byte counter is 0 that means person has just loaded, and network not ready.
every action it checks it against what byte owner is giving it and what it's recieved. It used as a buffering or timing pulse.
neat :3
really complex to wrap my head around but I'll eventually understand since I'm working with that stuff now xD
@strange token what are you trying to do, perhaps I can help.
I already figured out the above question because I was just overcomplicating it :3
I'm currently troubleshooting ondeserialization not running in my editor testing
does serialization work in your client sim?
yeah it doesn't work in client sim
hang on. tagging @meager quiver to ask if that script they gave me will work for @strange token
what's it do O.O
makes Serialization work in client sim. but it's kinda specific to the way I set up my programming in my NPC project. so IDK if it will work for yours.
ohh okie
I think I'm fine with all my network testing having to be in-game, it'll make sure I make less changes that are broken out of editor
you can test them in client sim by manually activating the events in the inspector when client sim is running.
im gonna give you the script anyway. hopefully it fixes it.
@frozen igloo is this a good fix? ^ @meager quiver wrote it.
Tap and PhaseDragon know WAY more than I do.
Sorry I'm busy and don't have the spoons to look into this right now, but I wanted to acknowledge that I got your ping, so you're welcome to ask another time if you haven't found the solution by then
The script I made here has some edge cases - it doesn't perfectly replicate networking, it just does it correctly enough to work with the networking style that your script uses - it may fire events incorrectly for some people depending on how they designed their netcode.
The tricky thing is having synced variables behave as expected, since you only have one copy of the script running, so to simulate this correctly you have to stick with a single way of signalling stuff, and that can break with other peoples prefabs etc.
Still, worth a shot, if nothing else.
The ultimate issue is data appearing before the events fire, as well as specifics around how long it takes for events to get through given this (immediate vs next frame), which for some scripts can result in a malfunction. Yours doesn't with this script.
It's just close enough that it works for your needs.
@strange token There you go ^ theres your answer.
how long in average does it take for a player to fully gain ownership of an object?
im using cyan object pool to give an object to each player, and then set them as the owner
but once that player becomes the owner, i need to set the owner as a variable on the object for it to be used in an update loop shortly after (since i wanna keep it optimized, instead of always calling getowner)
im considering just delaying the setting of that variable to once the transfer of the owner is finished, so just a timed delay should do
https://creators.vrchat.com/worlds/udon/networking/
sounds like you want to use OnOwnershipTransferred(VRCPlayerApi player)
Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
well i thought of that, the issue is the owner wont always be transferred. What if its on the right player right away? like if it was the first player to join
or if a player left and now all remaining objects that dont have a player for some reason go back to the master?
im not sure how to capture all these unknown factors
this feels too bug prone to my liking
what are you trying to achieve? why not VRC PlayerObjects?
well... I tried those and it basically requires me to turn on domain reload... which adds like two minutes to every play mode start 😅
so im avoiding player objects until i really really need them, and even then ill probably make a system to nuke them in editor until its been built
?_? just fix your unity bro, you are hamstringing yourself and then trying to work around it
yeaaaaaah youre not wrong.
But i dont wanna turn on domain reloaddddd
what should i do?
Disabling Domain Reloading
To disable Domain Reloading:
Go to Edit > Project Settings > Editor Make sure Enter Play Mode Options is enabled. Disable Reload Domain
i have it disabled right now, that allows me to have play mode start in 10 seconds instead of a couple minutes
what i meant is if i put player objects in my scene, play mode refuses to start properly unless i turn on domain reload
strange, i dont remember ever turning it on even
yet it works for you? odd
lemme try to reproduce that bug, maybe i can show you more
well uuhhh it seems to work just fine right now????
maybe it was just an issue in the persistence beta or something
how confusing, i remember spending multiple days scratching my head around it 😅
ill let you know if it pops up, thank you!
lol
yeah no i restarted it a couple times
only got some weird one off invalid unity objects but nothing outright preventing me from starting play mode
@fallow mountain i got the bug again
MissingReferenceException: The object of type 'VRCPlayerObject' has been destroyed but you are still trying to access it.
This is the first error im getting, then all the scripts referencing the local player break if i saw that correctly
so basically everything
and i cant move my character in play mode so i imagine the local player didnt instanciate
VRCPlayerObject' has been destroyed ??? you gotta find who destroyed it
litteraly nothing destroys it
all i did was to add the playerobject component to a collider
Sounds like network IDs need to be confirmed, that interrupts the clientsim process
i see
double clicking the error redirects me to this line
In ClientSimNetworkingUtilities
You just need to open the network ID panel and confirm whatever message it's asking
can you take a screenshot of it?
Found inside the file ClientSimNetworkingUtilities
i tried regenerating scene ids, no luck
huh, that's odd. Usually that does it
oh wait
i cleared the ids, then tried regenerating them again
and i got this message
MissingReferenceException: The object of type 'VRCSceneDescriptor' has been destroyed but you are still trying to access it.
💀
where's that coming from?
well, the console
upon trying to regenerate scene IDs
this whole mess only happens when i have a player object in my scene
can you screenshot this complete error when you select it in the console?
copy paste is probably better haha
what script is it? what does it do
this
i mean what script are you running
wym?
this
this is the first error that pops up when entering play mode without domain reloading enabled and with a player object in the scene
which is, weird, since that didnt happen yesterday
😅
i mean can you post the script, what is the context of this code
ClientSimNetworkingUtilities?
yep
oh, i thought it is a script you wrote
it is not, both errors are from scripts that come with the vrc sdk
cant enter play mode nor regenerate scene IDs with player objects in the scene, if i have domain reloading disabled
is this latest SDK? and 2022 unity?
but it seems like this is the root of the problem @fallow mountain
coming from this line
in VRCNetworkIDUtility
i dont exactly understand this, but if i were to guess, its probably trying to assign a network ID to an object and fails, but inserts it into the array anyway
so when that gets referenced later, it crashes and burns
if that is correct, then something about domain reload fixes this
i mean, there is this network IDs array inside the scene descriptor
hmmmmmmmmmmm
hmmm
yeah looks like it, SDK problem, congrats (on end of identifying problem)
the funny thing is, i remember going up this chain of events all the way back when player objects were introduced
but you convinced me to look into it again XD
hopefully this convo helps those who have the same problem
lol it is not the ending it is the journey
or, it is not the result, it is the process
i forgot what the saying goes
maybe the sdk bugs were the friends we made along the way
i ironically really need the system im trying to make to work eventually, itll really make my game more responsive and fluid
and we get to blame VRC devs at the end of day
haha
can you not test in-game?
but yeah, tough choice
Do i take the player objects shortcut, and suffer the curse of x3 longer play mode loading times forever?
Or do i take the harder route and take the hassle of making my own system to mimick player objects
building? well that would be waiting 3 minutes each time instead
what system are you developing, can you not do it in a new project?
or new scene
on its own
so its fast
im making local colliders for every player
my items have functions that do things when theyre inside a player collider
at the moment im using OnPlayerTriggerEnter/Stay to detect when an item is inside a player
but the issue is that it only detects it on the "victim", not on the attacker
so due to vrc lag, its pretty common for the attacker to wonder why their hit didnt register, because the victim simply didnt see the item touch them on their end
i want to change this system to attacker focused instead of victim focused
and for that, ill need each player to have local colliders on every other player
pretty sure worlds like blackout have similar systems
since i never noticed hits not being registered when attacking
another option would be to use cyan object pool
but then i run into my original problem i posted about days ago haha
hmmmmm why not just use one local collider to detect? it follows local player and detects any remote player and send custom event when needed
how is that any different to OnPlayerTriggerEnter?
the victim would still need to be the one seeing the hit happen instead of the attacker
no difference, you just need one collider, on the attacker locally, when attacker triggers, check if the player entering is the victim, then do whatever it needs to do
yeah
so same issue happens
players are gonna be chasing each other and theyll wonder why their hits didnt connect
no because the detection happens from the attackers pov
oh? then i did not understand your explanation
how do i check the victim being inside the object if there is no trigger to detect it
the trigger is done by the attacker, rather than by the victim
or rather, by the attacker computer, rather than by victim computer
hm, lets make sure we have the same definitions
What do you mean by trigger?
my understanding of your problem is, if the victim lags, the hit doesn't register even the attack can see him attacking the victim, because on the victim's client, the attacker is lagging behind
correct
so the attacker needs a way to check on their own if the hit went through
without relying on the victim seeing it
so, do it on attacker's computer, so if a victim lags, they die easier
yep
yes
well that solved the problem of hit not registering
im trying to do exactly as you described, but i cant do that without running into what i described earlier
you can just use one collider for the local players, which can detect any player
what how?
the collider is entirely local, no need to sync, the only thing it needs to network is send custom event (victim dies or something)
and there is only one collider in the entire scene
and it is on the local player
on the attacker's computer, it is activated
on the victim's computer, it doesn't need to do anything, it can be disabled
the collider follows the attacker, locally, and detects any player entering it, when someone enters, do stuff
OnPlayerTriggerEnter...?
onplayertriggerenter is a global event isnt it???
?
ive been using an islocal check on it for so long i forgot it was globally fired
💀
yea your explanation makes a lot more sense now
major blind spot from my part, thank you for pointing it out!
happy hunting (?)
Yeah onplayerentertrigger is global because it outputs playerapi. No need to do that on s local only script.
yeah, i always knew that but been so used to not using it that way that i forgot about it
I REALLY wish that vrchat would have flagged global vs local events in the sdk. At least some different color coding im graph.
That will fire every frame they are within the trigger.
yeah i know, i meant using the groups like this haha
makes it easier to debug networking when quickly reading through code
like comments pretty much
Oh ha ha. I've resorted to a very strict network calling system procedure.
oh?
Yeah.. but it really only works well for my npcs..
fair enough
👀 that's all I care about... my little creations....my npcs
i totally understand that hahahaaa
i feel the same way
I can't wait till I get my trex up and running. 😈
Trying to get the combat system and sensory programs more refined though.
I'm kind of having trouble understanding where/when OnDeserialization is supposed to be "powerful" as all I'm finding so far is circular code that I can't straighten out lol
I don't understand how to cleanly set up a button that updates an udonsynced variable that does some things for both players while also having code that only runs for the person who updated the variable itself, without writing the same code in multiple places that seems like it'll become spaghetti code quick
I guess more specifically what doesn't make sense is why you'd run code in OnPostSerialization when that will always run every time you sync anything on a script
I'd rather put the sync-er's code responses directly after the code that triggered it, but I'm apparently supposed to just sync the values and run essentially the same thing other players run in OnDeserialization, but in OnPost, so that you run the exact same thing as close as possible to when other players receive it
but that whole "runs every time you sync anything part really confuses me on how you're supposed to really do anything in there
If someone could explain how this works and how you're supposed to pick out when code actually updates in order to only respond to changes to actively synced variables in OnPost that would be great 🙏
its used whenever you need to make sure every variable has been updated iirc
so lets say youre serializing multiple variables on the same script and you need all of them to be up to date before running logic
so it's not just supposed to be used any time you're updating any synced variables? ;_;
you can do that too, but if its only one variable id rather use fieldcallback
I thought OnPost was specifically to allow you to try to synchronize any synced variable updates with other players time-wise
idk i never used onpost
I've been programming with field callback for a year or more but I'm trying to learn the other way
ye
because most active folks here recommend onpost
but now I'm trying and actively dont' get it
what i do is just send a custom event after ondeserialization and also on the local player
basically does the same thing as onpost
i guess onpost is cleaner when you think about when things happen but in the end i dont think it matters too much
private void DoSomethingSynced()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
syncedVariable = newValue;
RequestSerialization();
HandleSerialization();
}
public override void OnDeserialization() => HandleSerialization();
private void HandleSerialization()
{
// Do stuff with the synced variables!
}
yeah so you skip onpost and just run the changes directly after requesting to sync
field callbacks are perfectly fine if you need to change a condition any time a variable has a new value
If you run your code directly after RequestSerialization, you will be assuming that the serialization was successful. If it wasn't, only the owner will see things working
With OnPostSerialization, you can check if the serialization was successful or not, and change logic depending on the success. Not only can you output the result for debugging, you could potentially "cancel" the changes if the serialization was detected as failed
it's a healthy way to keep things synced, and a singular place you can update and make any changes when the owner is meant to have it
running the code right after Serialization also makes the logic run that frame, and not when the serialization actually happens, which is the next network tick
This is what I was thinking and why I want to try working with onpost, but can you realistically put all your network sync code in onpost? I don't see how not to update variables in onpost since I can't say "hey result, what actually came in that serialization?"
You're misunderstanding OnPostSerialization
OnPostSerialization only runs for the sender. Everyone else gets OnDeserialization
right, I was planning on essentially cloning what remote players do in ondeserialzation into onpost so that both players run the same code after a sync
Having duplicate code is rarely a good idea.
But I guess you could call the same method.
unless it's just function calls
Yeah I'm searching around but not having much luck finding a tutorial that explains how and when to use the two methods lol
you should have a separate function that updates your states based on the current synced variables
then just call this function in OnDeserialization, and in OnPostSerialization if it was successful
This doc covers Networking Components, Properties and Events you can use in your Udon Programs.
what function you use all depends on timing, really, when you want things to happen
yeah this describes what each thing does but it's not a tutorial, doesn't explain why and when, just what
But, isn't the only time it would fail if you're exceeding the manual sync per serialization limit?
and if your arrays are null
kinda sounds like I've never had a good enough reason to switch networking methods because there isn't one xD
I always declare my synced arrays like this, which prevents that issue.
[UdonSynced] int[] _playerIds = System.Array.Empty<int>();
yeah that's all it takes
well now I have like 3 or more fieldchangecallbacks to write to make my code stop shitting its pants lmao
it's been nonstop sync looping because I didn't understand onpost lol
thanks for the answers all o/
Here is how I view it.
Request serialization: you call the mailman and tell them to come get your letter.
Preserialization: mailman is at your door waiting for your letter. You are jotting down last minute notes on your letter.
Mailman takes letter to mail distribution center.
Deserialization: mailman delivers letter to all your customers.
Postserialization: mailman delivers your letter back to you with a few things stamped on it.
Then everyone does happy dance 🕺 💃
Networking.DoHappyDance() ?
I'll have to practice that.
This is a fantastic way to explain it.
This is a really good analogy! I have one correction:
Postserialization: immediately after you give it to them, mailman confirms that they have received your letter and will tell you whether or not everything is in order before it is sent off
is it possible to have the letters sent out by mailman / OnPostSerialization success, but other people don't get the mail / fail OnDeserialization?
inside of onpostserialization, it gives you serializationresult. You can check result.success - if it's false it won't send, if it's true it should send. But either way you still get onpostserialization, so if you're expecting the event itself to be a confirmation of sending then that's incorrect
What happens when OnPostSerialization result unsuccessful, does it automatically request serialization again, or is the data abandoned (and have to manually request serialization again)?
you have to request again
if it fails one time, there's basically no chance it'll succeed just by trying again. There is something about your data that is invalid and you need to fix
So, OnPlayerTriggerEnter/Stay does not seem to fire whenever the trigger is on a VRC pickup, if it was a remote player entering the trigger
is that intended? if so how am i supposed to go around this?
the pickup layer does not collide with the player layer. This is intentional
right, forgot to specify
The trigger is on a child of the vrcpickup, which is on the default layer
remote players have a different collider, it's a small sphere centered on the feet instead of a capsule extending all the way up
oooh is that so
hmm, what could i do then? Am I forced to generate a new collider for every player that matches its size
if the default colliders for remote players are insufficient for your use case, then yes making custom colliders would work. PlayerObjects make that fairly easy
yeaaah well, you remember the issue i had with player objects yesterday XD
guess ill just make my own system
with cyanobjectpool or smth
Why is domain reload such a deal breaker? It's an unstable optional process that has the potential to break things, it's not too surprising that the sdk is one of them because a lot of the sdk was designed before disabling domain reload was a thing
because it adds two minutes to every play mode entry (my world is pretty heavy)
and i like to play mode a lot to playtest things
you should have a minimal test scene for cases which require going in and out frequently
youre not wrong actually
id need to set that up to cover all edge cases, i have a feeling im gonna have bugs that wont be showing up in a test scene due to their in world context
and then... yeaah itll be annoying to test
It's helpful to make sure your scripts can survive the jump between scenes because that shows you what is fragile and what the dependencies are, which makes for a more robust, reliable script in the end result
i guess i could start working only with prefabs from now on, that would make transferring anything from main scene to test scene trivial.
But then udon graphs wouldnt update properly (references often break inside prefabs), so id need to mass refractor all my graphs into sharp, time consuming task
agreed!
i had no idea graph scripts would make it so hard to refractor
Oh so it's probably good to have errors being thrown out on failed attempts on postserialization?
Like errors to logs
ye
Just Wana confirm something, when we request serialization, does udon check if data has changed or does it sync everything even if the data has not changed
It doesn't check
Thought so just had to double check. Ty
I thought this fails to build, huh
Huh, it does build. There ya go. I swear for a while it didn't, maybe that was unity 2019 era.
or maybe it was only on private stuff it doesn't? idk
Ah! It's just not available at runtime, right. Works fine in initial heap.
It'll not exposed to Udon if you try to use it in a method call.
OK so i have a network event that tells everyone to update the button owner's voice to be louder so ppl can hear them throughout the map. But its not updating for everyone and the voice never changes. Locally it does. I am confused where am i going wrong.
@hybrid kindle youre missing an actual network event and all this runs only for owner/button pusher. make each block into its own custom events (like, 'mute'/'unmute'), and after branch just call them via send custom network event - all.
there is an network event one its just offscreen
then its done in a wrong way. once again, left part up to branch should run only on new owner. right part (blocks) for everyone. so it cant be a continuous flow.
idk why you call network event initially. for me feels like it should start with just Interact (or local event if its ui)
its ui, which sends a local event, which sends a network event.
- Just a nitpick, why is mute=true going to louder volume?
- Can you show the networked event?
okay, but it shouldnt. network comes for blocks, before that its all local for whoever touch ui
- expirimenting
- I suggest renaming mute to isLoud (not a problem, just a personal preference)
- You gotta set owner before sending network event, because now everybody will run the event mutelogic and tries to set themselves as owner.
- Maybe u can do something about the on/off status not resetting between people, right now if someone turned it on, another person clicks it, it will become off (and do nothing)
And I am not sure owner update will be fast enough for other people to change volume on the new owner
tysm i will test things now
It may be more reliable if
- make s a variable "currentUser" and "isLoud"
- on interact (or custom event) to set local as owner, and
- if not already owner, set "currentUser" as local player and set "isLoud" to true, and request serialization
- if already owner, toggle "isLoud" and request serialization
- (for owner) and then custom event "UpdateVolume" which changes volume of "currentUser" based on "isLoud" true or false
- (for other people) ondeserialization->custom event "UpdateVolume"
Why does the SDK care about None set UdonSync objects now?
And now ClientSim doesn't work anymore either... despite the networking tool now saying everything is fine?
Do you have objects with the same name under a shared parent?
Change them to Object1 Object2 Object3 and so on, and then regenerate IDs
@digital orioleSoon will be playable heh!
🤩 oh hellyeahh
what do you find is the best way to control the timing between those text transitions
The animation file itself, it also controls the sound effects (It only sounds off because of the compression output because I gotta keep the video file less than 10mb)
And the compression affected the audio lag for some reason lol
Anyone have any good documentation on how to handle networking for moving vehicles? I want things to be handled locally as much as possible and only sync important things such as direction changes. For context, a vehicle that doesnt turn and only goes forward or backwards. I want to do that, but I feel if I only sync the changes of torque, there will be small differences between all local players. I just don’t want to have any unnecessary loads on networks and I’m not sure if VRCObject synch is what I need because this vehicle has many other moving parts. (E.g a boom for a forklift or crane)
Object sync would be suffice for a vehicle, but yeah the forks would be a mind bender
You could sync the float that controls how high the forks are going but Im not sure how the physics would react
Assuming you would do something like that
Thats what I was thinking. The only solution my caveman brain can think of is maybe call a location sync every 5 seconds or so to correct any minor deviations and have all the variables synced when they are adjusted.
Im sure there are better, more optimized options
You also have the issue of dealing with owner changes, the forklift might be owned by the local player but if the cargo you are lifting is owned by someone else, you will likely have physics issues
Make a script that will auto update the cargo to become your object to counter this, maybe when the forks touch the object, get the owner of the forklift truck then copy/paste that to the cargo
Ill have to read up on that
Are you a graph guy or a programmer
oof looks like Im going to have de optimize my network usage beacuse I cant find a way to garentee the execution order of my network stuff without having to make the host wait 3 plus secconds betwen sending out syncs
It would be nice if you could bundle serilizations together and specify an order to read them in.
Programmer
Vrc object sync will sync transform and works for a vehicle. But if it has a predictable path, then yes u just need to a networked event to tell it to start or stop i assume
Are you trying to batch datat because you're sending alot?
just how much data are you sending lol. are you per chance doing well over 33 kb? considering you said 3 seconds. makes me guess 33 due to the 11 kb/s limit
Guys, who can help improve dropdown? One person helped me make a dropdown, but I would like to be able to take it all by any number, but here I can show a spoiler how I did it in my own way, I want to do a language translation, but here I got completely unoptimized.
So how well is the TeleportTo function in the vrc object sync?
What I mean is, how reliable is it?
should be as reliable as you picking up and moving the object by hand
Right, I am making a game where tiles are locally snapped to a certain point, but with non local clients ObjectSync, they have a habit of disappearing them.
So I'm planning on using that object sync TeleportTo that I mentioned so it would be consistent for everybody.
probably because of the smoothing makes it "overshoot", the solution is using FlagDiscontinuity()
Yep, that's probably it. I am learning. Thanks for the advice. 🙂
I meant in a format where you type in as many numbers as you need and paste them in, I think it's pretty difficult to do this. After all, I want to do more and more translations in different languages.
Wait a minute, I think I've found a better solution.
that’s called an Array. any variable type with [] after it is an array
I just thought it would be a nice thing to make my game use 7 to 30 bytes per interaction instead of 52 to 100. Ive basicly had to reduce the number of scripts. specificly scripts that had order of operation dependancy got merged.
how is it that my post serilization is showing that it was sucsefful if none of the other clients are getting any of the data.
No ownership issues not exceeding any limits and network is not clogged. But never happens with 2 clients. add one more client and it becomes an issue.
need more data
The GameObject with the synced script has to be active in hierarchy for everyone.
Are you absolutely sure the GameObject is enabled at all times?
I dont have access to the code where I am now but I'll try to describe the setup.
All network objects are always active and never disabled.
One client owns all the objects except a few and is the "session owner" All other clients only own there input handlers.
When a client makes a game input they serialize the object they own and the session owner only will preform the needed operations on the objects it owns by reading the clients input handler. A network event is sent back to the orginal client to reset there queue to a no input state.
There are 3 main code pathways that follow this model. Card play. Cancel attack. Pickup cards.
When 2 players are in game all pathways work at all times.
When 3 players are present all pathways work without issues except for play card.
Play card stops working on all clients except for the session owner. However I'f the session owner is the instance owner all pathways will work for everyone again but only 80% of the time for play card.
I have varafied through logs that. Post serlization is firing on the client requesting the input and 72 bytes are being sent. And that all clients actually own the specific object they serializeijg. I have also verified that no other clients receives the serlization event.
But the behavior tells me there is an ownership issue of some kind somewhere.
The only thing that stands out to me is that the working pathways start there execution from a script that has no network ties and the failing pathway is initiated from inside a script that is manually synced and not owned by the client using it. However the client is not trying to serialize that object it's simply using it as a way to talk to the one it is trying to sync data for.
Its also suspicious to me that the client is sending 72 bytes though beacuse the object its trying to sync only contains 1 byte so that may be a clue
I'm also thinking this may be an undocumented udon bug or behavior. Beacuse I've noticed some types of code entry points can changes udons tollorance for certain actions. I'm thinking I need to delay frames to make sure the scope of the event starts insides of a network neutral object like the other 2 paths
But it's hard to know when udon is not reporting on why it's not sending the data
But I'f any of those things were issues then I don't understand why the bug wouldn't be happening with just 2 players? I can't figure out what in the codepath changes just by adding an extra idle player
Root object game play state. Contains most of the data and functions.
Each card has a networked udon behavior. But the only synced value is witch player "owns" it on the gameplay side of things the session owner still has network control of it regardless of who owns it.
Each player also has a hand object that has the cards in their hand. Again session owner owns all of them on the network regardless of who the hand is assigned to.
A player object that holds some state data about the players status like are they still in the game or did they win.
And lastly the input handler. Each client takes and maintains owner ship of the handler corresponding to their player. Once the game is started there are no transfers of ownership for any objects
There are also several other client side udon behaviors that don't touch the network directly but are used to interface with it
For example the pickup and end attack functions are called from a local ui object where as the problem pathway of play card has been moved directly onto the card so that users can just click on the card itself to play it
In the last version of the game this functionality worked fine when the system was click card to add it to queue then click localui button to play to actually request serialization. But for some reason making this a one click prosses as broken it
No
The clients will each move them to the correct position client side only and look at what array the card id can be found in to decide where on the screen to move them
The position is not synched
i think you need someone to look at the project itself
probably too complicated for discord
The more I talk about it the more I think it's an undocumented issue with udons call structure. I think if I move the input back to local ui and just have the button teleport to the card when needed instead of having a play button on each card it will likely start working again.
I'm not realy expecting anyone to have a solution for me here I'm mainly trying to brainstorm possible reasons. So that when I get home later today I can have a list of changes to try
This is what the old version looked like without the bug
And that's the updated broken one it's literally the same code runing both the only thing that changed is what object initially calls it
Key questions:
Why are we trying to send 72 bytes of data to sync a single byte.
Why is it being reported as sent successfully when no clients get it.
What impact does the player count have on this occurance.
Why does this not occur when the same code is simply called by a different object. (Speculation is welcome)
Also wondering if there's a way to tell if it actually didn't get sent or if the receiver is not listening. Might have to break out wire shark for this one
you can use OnDeserialization to check when the receiver gets it
have that print to console
I have confirmed that in the specific instance when the bug occurs no client not even then client that requested the serialization ends up entering there Ondescerilization functions. There is a log at the very top that never prints
the sender will never fire OnDeserialization
For testing purposes i put a call right after to see if it would run and it just stops there no script crash though
On post does run and reports a sucsess with 72 bytes sent
But there's only 1 byte to sync so I'm confused about that number too
iirc, serialization packages all udon behaviours on an object and sends them all together
Well in that case then there's only 5 bytes total
are multiple objects requesting serialization in the same frame?
Nope just one
Although that info does help and it's created a lead I'm going to follow up on
Still when I think about it we should only have one byte
So we have a card object with a 4 bytes of data on it that could be synched
We enter code execution through the card via clicking on it
We do not attempt to sync the card but we insert its id into an array of size 1 that exists on input handler the data type for that array is byte btw.
Then from inside the scope of the card we request serialization on the input handler
That handler is on its own game object with no other scripts
I also for most of my scripts I don't call request serialization directly. I mark them as dirty and at the end of every cycle it calls ones for each dirty script so no double calling is possible.
In the case of input handler only a players click can trigger its request serialization
72 bytes sounds like a lot more data is being synced, like an array or something
The only script that has that much data is the root play state script.
But none of that code is Evan getting the chance to fire off yet as it basicly stays dorment until the session owner runs Ondescerilization for the clients input queue witch is proven to not Evan get entered.
We basically have a simple function that looks something like.
Public void OnClick()
{
If(VALIDATIONPASSES()
Handler = state.GetplayerbyId(localplayerid).inputhabdler
Handler.queue[0] = id
Handler.request serlization()
Is essentially what's happening although trying to type it on phone didn't work out perfectly
So in the error use case we don't end up interacting with very much of the project
We only ever touch the state in the form of a helper function that helps us find the correct input queue we make no attempt to modify its data
and the player clicking is also the owner of the Handler script?
Yes
They don't own the card
But they own the handler they are requesting on
And it's the same deal on the server side the server does not own handler but it operates from inside its on deserilization and is able to sync the stuff it owns while running in there
And there's no bugs on that side of the runtime in addition there's 2 other pathways using the same model working 100 percent as intended
I've said this before but there's kinda contradicting evidence.
On one hand it's acting as if we have an ownership issue. Beacuse the bug does not occur if the instance owner takes on the session owner role of the game.
However then why is only 1 in 3 parts of the system being affected if that's the reason?
On the other hand it seems like an entry point issue but again if that's true why is it not an issue 100% of the time
when manual syncs get fiddley like this, I sometimes wrap the RequestSerialization in an additional ownership check
I do a check if the local player is also the owner of the UdonBehaviour I'm about to request serialization for; if true, request as normal, but if false, log an error like "attempted serialization without being owner!"
maybe good to print further info like who really was the owner, etc.
Yea I've got that going too but it's. Not tripping
Some how owner ship of the session owner is affecting the behavior of objects that it's not supposed to and never owns or trys to modify
I wish udon would log more of it's decisions
do you have a debug that shows what player is the owner of the important scripts in-game? Or can just RShift + ~ + 3
Is do the shift plus 3
But the way I have it set rn is the session owner is always the player on the south side of the table
that makes sense
I was worried the master was taking ownership of something unexpectedly
Nope at the start ownership of everything it set to the session owner. And then after that players find and take ownership of their own input handler no more transfers hence fourth
So if there is an ownership issue there's exactly 1 function to look at
But I couldn't find any issues with it
and this is breaking when there's a third player in the world, and works fine with just 2 players still? or was that an earlier condition
Clarification a 3rd player can join the world and it will work if there's only 2 playing at the table
But as soon as a 3rd joins card play functionality stops working for all except the session owner
Funny enough all players can still pickup cards witch runs the same way as playing cards
I could mess with ownership of the input handler ping pinging between the session owner and it's player but I'm not sure if that will have an effect
Are ya going to be around in 5 hours from now @twin portal
I'll be free to be in-game in about 6 hours from now
I'll hang out in chat here all day though
Was thinking of popping on to vc streaming the project if you Wana be another pair of eyeballs
Also thinking of pushing the lasist version up to the hosted world to test live with
The orginal old version of the game will still be there to so won't stop anyone from playing the working version
@brisk magnet are you making a card game? or what are you making with cards
If you scroll up you'll see some example videos I put in the chat
Also last weeks build is playable here https://vrchat.com/home/world/wrld_2340fa50-a828-4a24-90b9-40718a0c3bb1/info
@twin portal found the reason but not sure how to fix it.
so whats happening is in this function the array is ending up a null with no length and thus causing the syncs to fail. and im still extra confused on why the player count is affecting weather this ends up with a value or not. any ideas?
any arrays you want to sync should never be null at any point
like during declaration
ive made sure to never leave it null before sync. its only allowed to sync if it has a value as its pretty much the only value.
In any case I fixed the issue with it nulling out however that wasint the issue its no longer null but the same bug described before is happening regardless
even before syncing, at any point in the code is it null
ideally it only matters if its null when you sync, but I've had issues with synced arrays if they were null at ANY point of operation
that should be good enough
it would be obvious if that was the issue anyway bc the serialization would fail, now that I think about it
but you said it was succeeding
it is now
but as you can see the session owner on the top window didint get a single thing
theres debugs in every single post pre and onDesc
hold on
you're calling OnDeserialization directly
didn't we suggest like, not doing that
thats not executing in this case as that condition is false in this patheway ironicly it works fine on the paths where it will hit that
This bug happens only for clients that are not the session owner
in anycase it dosint matter so I updated it
and no change. that hand on data call was from the previous move that player was able to make before the bugged player attempted to move
I think I might just sawp back to my old UI its starting to look like I cant figure out why its behaving how it is. And realy dont understand how a simple UI change radicaly changed how the network behaved
does udon have a max call depth?
so interesting development.
this change allows the code to work on the client but not the server. in theory I can just make an if statment and then It would work for all clients now but this feels so wrong there has to be a better way then this
Udonsharp does not, because of how it has to do recursion through stuffing each layer into an array.
The way that udon 1 operates as a C#-level VM, it never truly goes that "deep" on anything, as it's backing out and then coming back in on every single operation.
All of these technical decisions contribute to the overall speed of udon having large overheads above and beyond most standard C# implementations. As a result, each iteration costs dramatically more than a traditional call system which does all that on the stack, but the weird upside is that it makes udonsharp able to handle waaaay more recursions and call depth than most implementations.
That said, I wouldn't be surprised if other languages have their own ways of dealing with call depth and stack overflows, but I'm not intimately familiar with what's been happening out there.
Regardless of where we're at now, this all means that the eventual successor to udon 1 will very likely take the form of something with a more traditional callstack, which would bring significant performance improvements but at the cost of losing that kinda useful relatively-infinite callstack that we have here
Udon graph technically has native recursion support to insane depths simply because of how all communication from one method to another goes through a class-level event. It's clunky without sendcustomevent with parameters, but it's been there the whole time.
thanks for all the info. allthough im not realy having preformance issues. just scope not being maintained or executions being skipped its hard to tell as udon is not throwing any errors
it seems to realy not like this location as the entry point for the script im talking to
Oh, is it actually recursive? Or does it ever have a function that loops off to some other script and then comes back to run the same method call twice within one "stack*?
no its a very simple function just one object calling another then asking it to sync
somthing about the call structure is just going very wrong
and for some reason being the owner of an object not part of the equasion or shouldint be part of it at least is somehow affecting witch clients can use this one
Fascinating, what's the full picture of events flowing into each other in that case?
Would it be possible to pull up a screen share and show you?
Ah not at this moment but I'd be able to find some time for that later tonight
ok in the mean time ill try to give an overview
so the main thing that holds the game together is a class called DURAK play state.
One player is selected as the session owner they get control of all udon behaviors except for 3.
There are no ownership changes once initial ownership is assigned. each player has a input handler object. they write the server reads
so when I play a card. I click on the card that the server owns, then the card looks for that players input handler and tells it to serilize after inserting itself into a synched value.
then the player acting as the server will read the handler and modify the state from inside the input handler. this works fine beacuse we are only reading it. while requesting syncs on stuff we own.
theres 3 pathways for this. Play card. Pickup, end turn
Play card runs correctly on the server only.
Pickup and end seem to run perfectly on all clients.
I skipped over allot but thats the basic gist of the situation. On post is firing on the client but the server never gets the event for the data
id also like to note there was a previous version of this game where all of these features with the same code worked without issue. but I changed the UI for playing the card and thus had to move the script entry point
Im trying to figure out why it was happy with the other one but un responsive with this one
Oh, your callstack for that is jumping back and forth between a couple people, does that mean sendcustomnetworkevent is utilized as a core piece of the callstack?
In other words - is that more of a characterization of how it operates between scripts, or is that a direct description of the callstack bouncing between scripts?
no I only inserted it here beacuse im just shooting in the dark to see what happens no where else in the code is it used like this
It seems to be helping in the particular case for some reason
we request on the input handler. server responds all other clients ignore the event. server seralizes another object. all clients take it and react to the data
so its just one quick ping pong
however ive found for the clients in particualr specificly on the play function delaying the call by 30 frames allows the signal to get through
where it would normaly report 70 bytes sent but not actualy send anything
im also woundering why its costing 70 bytes to synce a byte array of size 1
Ok, I think the most thorough way to answer this question is to link a couple hours of GDC sessions on gameplay networking - if you're down for that let me know!
If you're looking more for a quick fix to this specific thing, I would also be down to walk you through how I would approach your specific situation. If that's more of how you operate, I'm down to follow that lead further.
id honestly like to take both options. on one hand id like to get this fixed soon. on the other id also like to know exactly what was happening long term.
I plan on making a few more worlds and there not going to get any simpler XD
side note I made abit of progress
the server is actualy getting the event now but no data still
Ok, I'll be able to screenshare with you in a few hours if you end up wanting to do that, but in the meantime while you wait, here's a couple videos to watch:
https://youtu.be/h47zZrqjgLc?si=fE1mRahMV3sfu7S
https://youtu.be/Na95i4ZD68I?si=MQu-ON9faV3RYBBoi
In this 2011 GDC session, Bungie's David Aldridge discusses the programming that drove Halo: Reach's online networking.
Register for GDC: http://ubm.io/2gk5KTU
Join the GDC mailing list: http://www.gdconf.com/subscribe
Follow GDC on Twitter: https://twitter.com/Official_GDC
GDC talks cover a range of developmental topics including game des...
Presented by Wayne Coles at SDC 2023 as part of the Code stream
Most games have some form of network functionality, and most game-engines come with a framework for serialising across the network.
Network teams will generally focus on friends, matchmaking and other online services.
This talk dives into network practices for games written with ...
The extra bytes are something we call "header".
Basically, what are we sending, how big is what we're sending, where should it be delivered, etc.
i figured it would be, but the header is just data type size and a few other things right?
I'm not certain about the exact contents of VRC's headers.
Header is one part of it, but byte alignment is another that takes up more
I need to test more to confirm but I think this fixed the bug
do you have any idea why this worked?
forgot the bottom
again session owner is just the player the owns all the non input handler objects
(the reason its an array is im considering allowing multiple inputs per sync)
yea defintly wana try to get rid of or reduce that 30 frame wait its pretty ugly
but its confirmed that it works now so thats a step forward at least
also funny thing is apprently those are allready in my book marks as of last week and I was partway into one of them
Must have been going down the right track somewhere then 
I mean I had no doubt I would eventualy find a solution. I mainly struggling with understanding why udon does what it does in certin situations.
but in this case Im not realy ready to accept this as a solution beacuse it comes with an ugly 30 frame wait and I dont realy know why it works.
and for all I know theres somthing wrong with it thats going to cause more issues in the future
I mean I had no doubt I would eventualy
A lot of languages including C# actually support a thing called tail calls https://en.wikipedia.org/wiki/Tail_call which means if you're doing a recursive call as the final call of a method, you can actually just discard the stack since you won't need to use any of the old values from the previous call. This lets you do infinite recursion on functions that are written to allow it and it ends up being required for a lot of languages that do functional programming in order to allow iterating over sets of arbitrary size using recursion.
I remember learning about that in college on how to optimize recursive calls
When i was in collage they docked us for using recursion in allot of cases xd.
In my orginal question I was referring to call depth meaning how many layers of references inside of an object can I go
In some ibm systems I've worked on they lose track after like 7
poor things....
Working on an as400 was like the the weirdest thing
You needed 4 languages per program beacuse each labguage has such harsh limitations
Yea you know the pain then
Of having to go back and fourth between rpg clle dds and sql lol
And piping it all down a websocket talking to something running c++
we certainly take a lot of modern software development for granted
Oddly enough certain specific things in udon have been giving me more grief then the ibm stuff ever did but it's not an un familiar thing. There's a saying "the reason so many languages exist is beautcuse all of them are garbage in some way" lol
The grass is always greener ya know
stares longingly at Nanite....
I can't say I've had the pleasure or pain of using that
Btw @twin portal or anyone elss interested. Would you be down to help debug in an hour i got a long gg list of tests to try and having another pair of eyes take a look could helo
I won't be available unfortunately
That's ok
WOOP I fixed the big bug now its back to fixing the normal bugs
Read up on a lot of the convo, fascinating
I havent seen everything you posted, but id be down to help!
Id certainly need you to resume your situation so that we can analyze it together cleanly
I sorta already thought Udon did tail recursion lol
of course never tested or proved it
bug is fixed but if you wana come and test the uploaded version I need to confirm it still behaves the same on the real vrchat network
I need at least 2 people other then myself to be fully sure. beacuse when I first had the bug there needed to be at least 3 players to trigger
also if possible enable your debug UI before you join. if anything weird happens a screenshot of the logs will help me out
you can use vrc quick launcher for this
Im not familair with what that is. but I sent you a link it should bring you to my instance
The VRC Quick Launcher is a tool that helps you launch multiple profiles in the same instance of VRChat while also providing an interface to modify the debug options. This avoids the issue of having tons of shortcuts.
allows you to start multiple clients
essential to debug networking
is this differant then using the SDK window to spin up multiple clients?
and is there ping simulation. beacuse ive had bugs that dont show up unless theres a little bit of latencey
doubt it
but if you suspect something to behave differently with ping, i can help you by pointing it out in your code
ive had a lot of issues with it myself in the past so i think i have decent experience in finding em
thanks that info will defintly. will still want to do some live tests for final validation.
ive allready done an hour of testing with multi cleint and it all checks out but I know theres a chance in the real network somthing will still go sideways
and theres kinda 4000 lines of code its not exactly a quick scan over XD
although its planned to be cut to 1700 after some more refactoring
fair XD
ngl at this point youd think it would be encapsulated into easy to digest functions
well funny thing is it was
i see
but then I flattened it beacuse the bug I was hunting was to weird
and the bug itself seemed to be partly from to many function calls from other referances
so I think refactoring the code might just bring the bug back
ill have to see
like i said i havent read it all, but from what ive seen, if youre having issues with race conditions and networking, using OnDeserialization may help to make sure you have all your variables in order?
just shooting that in the dark in case it helps
yea if u want I can share screen the project and give you a little overview of the differant execution pathways
customnetworkevents are just a bad idea when you rely on things coming reliably or on time
Id love any feedback
sure! im busy atm but ask me again later!
allright
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Coil_Pedestal_Logic : UdonSharpBehaviour
{
[SerializeField, Tooltip("Pool of Objects to be used.")]
private VRC.SDK3.Components.VRCObjectPool objects;
private int CoilType = 0;
private string userDisplayName;
void OnPlayerJoined(VRCPlayerApi player)
{ /* When a player Joins, we should first check a hardcoded list to figure out if they have a special coil. if they aren't apart of this list, we spawn in the normal coil. */
userDisplayName = player.displayName;
Debug.Log("Local Player Name: " + userDisplayName);
Debug.Log(userDisplayName.Equals("Galexion"));
GameObject PlayersCoil;
if(userDisplayName.Equals("Galexion")) {
CoilType = 1;
PlayersCoil = Instantiate(objects.Pool[1]);
} else {
PlayersCoil = Instantiate(objects.Pool[0]);
}
PlayersCoil.SetActive(true);
Networking.SetOwner(player,PlayersCoil);
}
void OnPlayerLeft(VRCPlayerApi player)
{ /* When the player leaves, we need to destroy their coil. */
}
}
I am having issues trying to sync objects, I've added the correct Components to the item I am creating. I'm able to pick up and interact with the object(s) but others are not able to. above is the code I use to create these objects, using a object pool as a way to reference multiple objects without adding a additional variable for each item I want to add.
The reason why I am NOT using VRC Player Object is due to how I need to spawn the coil, as I don't want to try swapping the mesh at runtime, and I have Special SFX & Particles for each Coil, which I don't think I can switch out effectively.
Expected Result:
- Coil Pedestal Spawns a Coil and sets the Object Owner to the player who just Joined.
- Player is able to pickup the item and use it.
- Other Players are able to see the item be moved.
Result:
✅ Coil Pedestal Spawns a Coil on Player Join.
❎ Only the Instance Owner can grab and use all Coils Spawned, instead of just the coil they were assigned.
❎ Other Players are unable to see the item be moved.
Hi. I was wondering if there's a way to fix this.
I think the recent udon network update broke my map since how my script works is like this:
AVelocity = new Vector3(24 1, 11);
RequestSerialization();
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(DropObject));
Basically i tried to sync my velocity and call **SendCustomNetworkEvent **after. This way i don't need to use **OnDeserialization **etc since before the network event is called, the synced variables gets synced first.
Now **SendCustomNetworkEvent **gets send without the variable syncing first.
I know this is not the proper way to do it so is there a way to fix this? Thank you
you are using Instantiate to spawn the object. Instantiated objects cannot be synced.
You could possibly use TryToSpawn from the pool, but this won't give you a specific object in the pool, it will return a sort of random one. ObjectPools are designed for multiple copies of the same object, like a single type of soda from a vending machine. It doesn't matter which one gets dispensed, TryToSpawn just gives you one and syncs its state with other players
the order that a serialization arrives vs. a custom event is not guaranteed. If you need variables to be synced before running a function, you have to wait for OnDeserialization. Otherwise you'll get the behavior you're describing; the CustomNetworkEvent arrives before the variable has been synced
running RequestSerialization(), and then a CustomNetworkEvent, does NOT guarantee the data will be synced. Because the data is not synced that frame, it is synced on the next network tick, which is usually a few frames later
is there any way to do it akin to what i'm trying to do?
there's a few ways
if you want it to kind of act the same, you have an array of GameObjects, and just at start have these objects hidden away somewhere. Either put them really far away (easier to implement, but if you don't put them far enough away, people could just boundary break and go grab them), or have the objects hidden and start and sync if they should be visible or not (basically manually do the job the ObjectPool is doing)
Why are you so insistent on doing it wrong? There must be a reason, but you have not provided one.
This makes it very difficult to understand your situation so we can help you.
Like being insistent on eating soup with a fork and wonder why it isnt working half the time
what is this variable and do you really need to sync it, can the other people calculate on their own?
this is an amazing analogy
btw, anyone notice network events feel faster after the update, or is it placebo
it's not! there was some network optimizations in one of the latest updates
Several internal network optimizations.
Observed behaviour for users and creators (in Udon) should not change. Please open a bug report if you encounter any problems!
This includes a full rework of how SendCustomNetworkEvent works internally, which is a pre-requisite for Udon Events with Parameters (check our dev update for more). For existing content, you may notice slightly improved throughput or latency, but otherwise full compatibility should be maintained.
network events with params is gonna be revolutionary
i feel like regular SendCustomEvent with params would be more useful, because i rarely use SendCustomNetworkEvent
especially DelayedSeconds
i often need DelayedSeconds with params
i used to use SendCustomNetworkEvent in almost every project, but that’s because i used to do my networking improperly, and i thought sending network events to sync a bool was fine
fr, i dont know if there is a way to delay execution without eventsdelayedseconds/frames but if there isnt its our only way of doing it
you can wait in fixedupdate using fixedtime, which is a little different than update (not really related to networking)
or... OnPreSerialization
if you are syncing stuff
i guess it dpends what you want to delay for
Thank you for the explanation I appreciate it! I just wanted to know the reason how it worked before so that answered my question. I was new before that's why I didn't use OnDeserialization and had 0 knowledge with. It's good now i started to implement OnDeserialization.
that optimization in the latest update may be what "broke" your script. The SendCustomNetworkEvent is now more optimized and faster, so it arrives sooner than the serialization, compared to before
Yes I did notice that! It's really good that it broke or else i wouldnt fix my janky script. Thanks again
THis has been driving me crazy I have done the same previously to network datalist data and Im am not sure why this just keeps not syncing the data I have been messing wi it for a day at this point
public class RuneCircleTrigger : UdonSharpBehaviour
{
public GameObject cloudRunesHolder;
public GameObject pillarRunesHolder;
public Sprite[] AllRunes;
public GameObject[] cloudRunes;
public GameObject[] pillarRunes;
[UdonSynced]
private bool runesAdded = false;
public DataList runes;
[UdonSynced]
private string _runesJSON;
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
SetPillarSprites();
cloudRunesHolder.SetActive(true);
pillarRunesHolder.SetActive(true);
}
public override void OnPlayerTriggerExit(VRCPlayerApi player)
{
cloudRunesHolder.SetActive(false);
pillarRunesHolder.SetActive(false);
}
public override void OnPreSerialization()
{
if (VRCJson.TrySerializeToJson(runes, JsonExportType.Minify, out DataToken resultQueue))
{
_runesJSON = resultQueue.String;
}
else
{
Debug.LogError(resultQueue.ToString());
}
base.OnPreSerialization();
}
public override void OnDeserialization()
{
if (VRCJson.TryDeserializeFromJson(_runesJSON, out DataToken runesJSONResult))
{
runes = runesJSONResult.DataList;
}
else
{
Debug.LogError(runesJSONResult.ToString());
}
SetPillarSprites();
base.OnDeserialization();
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
if (player == Networking.LocalPlayer)
{
SetPillarSprites();
}
}
private void Start()
{
runes = new DataList();
if (Networking.LocalPlayer.IsOwner(this.gameObject))
{
if (!runesAdded)
{
DetemineInstanceRunes();
SetPillarSprites();
}
}
SetPillarSprites();
}
public void DetemineInstanceRunes()
{
runes = new DataList();
for (int i = 0; i < 6; i++)
{
runes.Add(new DataToken(Random.Range(0, 10)));
Networking.SetOwner(Networking.LocalPlayer, gameObject);
RequestSerialization();
}
runesAdded = true;
Networking.SetOwner(Networking.LocalPlayer, gameObject);
RequestSerialization();
}
public void SetPillarSprites()
{
Debug.Log("---------------WHY MOFO----------------");
int i = 0;
foreach (GameObject pillar in pillarRunes)
{
if (runes.TryGetValue(i, TokenType.Int, out DataToken index))
{
pillar.GetComponent<Image>().sprite = AllRunes[index.Int];
i++;
}
else
{
Debug.Log(runes.ToString());
}
}
}
}
I see you're using requestserialization and that string is bound to be too big for continuous, are you certain the script is set to manual?
I highly recommend forcing the sync mode on your class with this class attribute [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
I use manual out of the gate in most cases but, yea i checked that a few times lol to make sure i wasnt crazy
And you're 100% sure the GameObject with your script is active in hierarchy on all clients?
Yea its on the trigger there isnt anything that toggles that object or anything like that
Actually moved that logic to only try to use the data for when entered trigger too so it would clearly be active at that point too and still the same thing.
Ah, here's why: Datatoken numbers serialized through JSON lose their tokentype, they will always become doubles because JSON doesn't store what number type it is
when you do runes.TryGetValue(i, TokenType.Int, out DataToken index)) that always fails because remote users will never have ints, they'll be doubles
I'd use the TryGet that doesn't specify type, then do something like MathF.RoundToInt((float)index.number))
Gahhhh, that's a rough one. I can fix around that for sure. Thanks Phase lol. Let me play around with it a bit.
is synchronizing an array of 128 bools the same as synchronizing an array of 16 bytes, in terms of how much data it sends over the network?
no, a bool takes a full byte when being sent over the network
even in an array?
would assume so
It does not.
Unfortunately.
yeesh... alright, thank you!
ok yeah, I couldn't resist checking -- 128 bools is 192 bytes, 16 bytes is 80 bytes. so interestingly, it definitely compresses to less than a full byte each, but, not as much as if I manually pack and unpack them myself before transmission. that's too bad
actually, I wonder how much of that is built-in overhead. lemme try with bigger numbers
the built-in serializer cares more about speed than packing size, so yeah it's not too surprising that a manual pack can make it tighter
ahhh, I guess that makes sense
huh, when I turned the byte array size up to 2048, it still only says 80 bytes in SerializationResult, even if I fill the array with random numbers so there's definitely something to send?
trying to make a simple report anyone know why instance_owner is ending up with a null value?
Instance owner refers to the person who made the instance, so there is no guarantee that person is currently in the instance let alone whether there even is someone who made the instance. Group and public instances do not even have a singular instance owner so it's always null
If you want a consistent client to hold ownership of something, you probably want the instance master instead. Instances always have a master, because when one master leaves the responsibility transfers to someone else in the instance (and avoids quest users if possible)
ohhhh I thought instance owner was what instance master was I see now
structuring networking around the master is usually not something I'd recommend but if it's unavoidable or you're already locked into that structure, go for it
yea my network is designed to not care who master is but for some reason the master is having an impact it shouldint hence why im making this to confirm somthing
OH heck, even though I raised the array size in the source, it was keeping the old size in the inspector. no wonder
ok yeah, 2048 bytes is 2112 bytes, and 16384 bools is 16448 bytes, that makes sense. if it's for performance reasons then I guess I'll just pack it myself
If im getting a sync failure but cant identify the cause, are there any more detailed logs that might tell me why that I cant get in the client? Post serlization is reporting that the sync was sucsessful but it definitly failed.
Is it possible to have a script that mimics the player api?
I recently got inspired by the recent Minecraft horror mod known as The Broken Script. Where in it, your world is haunted by old minecraft players who got trapped in the game and slowly corrupts the world. You'll get player join messages, entity encounters, random structures built on the world, etc.
I wanna do something similar with one of my worlds. I wanna make it so on a very rare chance, the player join notifier says that a player joined. Their username is just a jumbled mess of numbers and letters. You'll occasionally get cryptic imagery and messages on the walls and floor like as if they're using stickers to communicate. You could even occasionally see them lurking outside the boundaries, watching you. Their nameplate bugged, their avatar never loading.
Is this sort of thing possible? Have a script that triggers other scripts that detects when a player joins, and have it display a custom name?
not in the way you describe. you can however manually create all those effects
you can simulate with your own custom ui, custom nameplate, custom models etc
but you cannot make playerapi actually malfunction as you describe
@meager crystal youre not really supposed to mess with game's existing ui in any way by tos
unfortunately the broken script is too cool to genuinely create in VRC, but if you’re able to create your own UI and stickers and such, like they said, that would be okay. just don’t recreate it too perfectly, that might get back into the TOS territory
maybe have a custom join message thing, like M.O.O.N’s join system, look messed up instead of the official one. because that prefab was very popular for join messages
I am wondering if I should use OnPostSerialization and OnPreSerialization. I guess it only gives a result back and doesn't really help with network sync?
you'd use it if you need to use it
OnPostSerialization at least is good to have it print out the result of the serialization, so you can get a log if something is going wrong
So it would look something like
As object owner that changed synced variables:
OnPreSerialization();
RequestSerialzation();
Then it gets a result back?
you don't call these functions directly, they are events
OnPreSerialization runs after RequestSerialization anyway
If you guys don't mind I'd like to put an idea i have here
Iv been thinking about adding players to a list/array again, something iv never been able to crack in years. Iv bene trying to think of work arounds that avoid the whole list thing and just focus on disabling game objects instead like this
turning on a trigger and having a player step in it to trigger the event for them (will probably work but isn't really clean or efficient)
Another one was maybe making using of player objects to stick a trigger to a players wrist or something then using a separate game object that collides with it to send a event to the owner of that object (not even sure if that will work)
Maybe for the list I can Use a foreach loop or whatever the method is to get all vrcplayerapi in the instance. Then construct an array of player ids
Pressing a right or left arrow will cycle through the array and display the name of the player associated with that ID
On player join/leave will reconstruct the array
Have a manual button for reconstructing too
Pressing the button in the middle that shows the players name will.. make them the owner of a game object then send out a networked event targeting the owner of that object to make them disable/enable some game objects
Some concerns I'm having too is what happens to player ids after someone leaves, if player 2 leaves does player 3 become player 2 in the id sense? If so im assuming you would need to reorganize the array/list
Putting that all aside i need to break it all down and do things one piece at a time
The first thing I need to figure out, how do I find players?
I know i can use a trigger with ontriggerenter to find them
I know there's a way to get vrcplayerapis but I'm unsure how to do that one
If anyone is willing to help it would be appreciated
You use OnPlayerTriggerEnter to get the player that has touched a trigger zone. This event returns the VRCPlayerApi that touched it
You can also use GetPlayers to get all players currently in the world
yeah I'm not.... entirely sure why you'd need this system if you can just GetPlayers whenever you need to
That's no fun :(
Something like this would be perfect for catching players off guard and actually being scary.
I can't at all try to replicate a player in a vrchat world? Not even if I recreate the ui from scratch?
that's good to know, im just not fully sure on how to utilize GetPlayers in the context i need it for
i want to target a specfic player
so that i can do things like disable/enable some game objects for them, and stick em on a list
no. and for very good reasons. the only thing you can do is make an AI that follows a set of rules etc and when it should appear. or what so ever.
and beside that the broken script is a banned mod for a good reason too lol. it does a bunch of things that violate Privacy
But it won't appear to be the ghost of a vrchat player without the nameplate and loading diamond
Sad
Didn't it get re-released without all the computer related stuff?
are you talking about this https://creators.vrchat.com/worlds/udon/players/getting-players.
not that i know of. when isearched for it yesterday it showed it was still doing that.
yes, i believe so but im doing so in U#
id like to construct a list of the players and update whenever someone joins or leaves
then all you'd need to do is run GetPlayers when a player joins and when a player leaves, and store their playerID
I guess?
still unsure why you'd need an array
if you'd need to separate the players playing a game vs. all players, then it'd make more sense to have a list
but if you're adding and removing from the list when they join or leave, you're just recreating the results that are in GetPlayers
i believe I need an array for the idea i want to do.
I want to have a < button > (left arrow, main button, right arrow) and cycle through players. clicking the arrow will change the button text to the next player in the im assuimg array
not sure if this is even a good approach
I’ve made a system similar to this in an unfinished project, what about this is confusing for you?
execution
im trying to think of the approach i need to take in order to make this happen
is an array needed in order to do this? if so how do i add and remove from it and does it need to be synced stuff like that
my knowledge on this topic is iffy, im used to doing other stuff but iv always struggled with getting players and doing stuff like lists/arrays, teams or staff access
Yeah you’d use an array
You can either make it an array of strings for each player name or ints for their playerId
so the hard part isn't gonna be getting the actual list of players, it's working with the array itself, it sounds like
If you wanna save the list you’d use their name so it works between instances
its not meant to persist
as each instance will do its own thing
That helps, saving the names was a pita for me lmao
here' ill explain what im trying to do
I got my world Club Kitsune, I want to create a system where a player can become a staff member (add themselves to a list assuming they are a patreon or similar) then they will be able to add/remove other players to this list as well. it seems simple but iv never been able to crack it
I guess if you are making a list, it would be array
The simplest list maybe a dropdown menu
Simplest way is add/remove is OnPlayerJoined->add to the array and OnPlayerLeft->remove from array
It will populate the list when you join the world with everyone, etc
But it cannot persist in a difference instance, however if the list is sorted by playerID then it should remain the same if you rejoin the instance (except yourself getting a new playerID but everyone else should have same from before you rejoin)
being added to that list will allow them to do things like press buttons or access certain areas
yeah, thats the right idea
Yeah I tried setting up that exact system in my other world 😛 I’m a bit nervous to assist because idk if I even got the right approach or if I eventually got stuck with that too 🤔 my stress tests showed multiple names in one row occasionally and I don’t think I ever figured it out
i tried it many times before and it has broke in many ways, such as it just stopped populating the list, broke when master left, skipped players etc
i feel ashamed to say iv tried this on and off for like 2 years and have never succeeded
My idea for this would be to GetPlayers every time a player joins or leaves, then loop through and compare that list to your known staff members list and null out the player from the GetPlayers list if they’re staff, then you can loop through and display each non-null element to get an updating list of all non staff members
i just can't seem to see the bigger picture
That would accomplish the first half at least
hmm.. that could work
I’m pretty sure that’s already like a third as much code as I wrote to get that behavior 😂
what is your thoughts on this on the button with a left and right arrow to cycle through the names and display it one at a time
xd
How the buttons are selected and displayed is a bit of a UX decision, in my system I settled for two vertical lists with buttons in between to swap the selected button to the other column, which were essentially “allowed” and “not allowed”
Not telling you what to do, just giving more info so you can better decide how you want your UX! 😛
thats what my older one looked like too xD maybe it would be wiser to do it that way as it is more useful to know who is or isnt stuff
Okay so that’s either the right move or the dumb first move everyone comes up with 😂
does the int[] need to be UdonSynced?
true
no no, your UI should be a fully playable game of minesweeper. whichever player wins first is the one that becomes the admin
nah I think i should give ChatGPT admin and let him decide their fate
that's worse than giving it to nobody