#udon-networking
1 messages Ā· Page 8 of 1
Handle serialisation is just activating and deactivating gameobjects. And for the local player it works. It never gets to the other players.
Delete CustomNetwork(and custom event Handle serialisation), and replace Start with OnDeserialization
Hello, I am programming a VRChat world using UdonSharp (with SDK3) that includes a gaming table. I'm looking to implement a system that saves a player's preferences in VRChat so that when they return later, they can find and add to their score. Ideally, I'd like to be able to store a string containing "lastVisitDate/score/pot/cardObtained/etc" (to.string). š Another option would be to upload/download this string to a directory on my Google Drive or NAS. Does anyone have any suggestions? I've tried using: PlayerPrefs.GetString(playerScoreKey), but it wasn't accepted. Thanks in advance.
@wispy turtle you cannot arbitrarily store and request the data, you cannot use PlayerPrefs, its not in udon. All the urls to store/get data online should be either made on build (so its a constant link and readonly) or be entered "manually" via url input field by player every time (so in theory you can format this link automatically and make player submit it via urlfield like mysite.com/?user=amogus&score=356 but nothing stops the player from not submitting it if he lost some points from the last time or smth, let alone crashed without saving). Talk about persistentcy in worlds was for years now but nothing yet.
thas how i get it at least
Thanks for the response. Where can I find the commands for uploading and downloading a variable or array? Regarding security, we can't see the VRChat code in the headset, right? So we can't retrieve the URL. Anyway, it's just scores. If someone manages to cheat, we'll just adapt the game approach. It's not a big deal in itself.
@wispy turtle you cannot upload, you can only request some link. Some nodejs or smth server then can parse the data from that url and store it. But for this link to be custom it needs to be submitted by a player, you cant just do in udon "get 'mysite.com/'+PlayerName" or smth, it should be constant.
In UdonSharp, it's quite straightforward. Here's a simple example using Random.Range to pick a random number between 1 and 10:
int randomNumber = Random.Range(1, 11); // Random.Range is inclusive for the min, exclusive for the max.
is that you'r question ?
Conclusion: It's not yet possible to store a score to retrieve it for the next game. Did I understand correctly?
@wispy turtle generally yes, each instance is unique and theres no dedicated methods to store the data between them or upload smth outside.
Thank you very much for taking the time to answer me. Have a great day.
Well i did everything right but the only thing I have to work on is putting them in a certain order. That was my down fall.
The most common approach is to format the data as a string and then have the user copy the text so they can save it and load it manually themselves.
hey, im trying to use this package : https://github.com/Miner28/NetworkedEventCaller, but in the "Your code setup" section, it says to inherit from NetworkInterface. does that mean replace the UdonSharpBehaviour inheritance with NetworkInterface?
Yes.
ok, do I need that on a seperate script? the script i want to call and recieve the networked event from needs udonSharpBehaviour
wait nvm, i was looking at the wrong script lol
NetworkInterface already inherits from UdonSharpBehaviour.
ok
whenever i add [NetworkedMethod] to a method that inherits from NetworkInterface, it says the attribute doesnt exist
Are you using the namespace?
namespace?
Can you share a screenshot of your code?
Remove this line
ok
Your editor was a little too helpful and guessed wrong
ohh i see
You need using Miner28.UdonUtils.Network;
Great idea. Thanks.
šļøšļø
I do wonder how many people use the prefab these days 
https://github.com/Miner28/NetworkedEventCaller
I did release a new version today as well 
Uses variable integer encoding, meaning less data transfered over network
Next up's gonna have to be a project clean up and soonTM I plan to work on "TCP" and priority support as well as multi-method sending and player targeting
Once all that's done it'll be able to entirely replace the entirety of SendCustomNetworkedEvent xD currently it suffers with the limitation of about 8 events per second and it's "UDP" design
I'd love to know more about how this works under the hood! How does it handle the issue with vanilla Udon networked events not being lockstep with synced variable updates, for example? Does it solely rely on synced variables to operate?
And when you say it's limited to 8 events per second, do you mean actually 8 discrete event calls? Or some larger number of calls, but only processed in batches 8 times a second? (Sort of like how normal Udon network serialization events occur 5 times a second)
It solely relies on synced variables, from my testing ~8 serializations per second is something the UdonNetwork can handle and catch up to if its not for too long of a sustained time.
It currently is made without reliable networking in mind (meaning there is no checking for skipped events) However, me and a decent list of people have been using it extensively in our worlds and the drop-late is sub 1% for normal use-cases.
I plan to solve this issue of it having a chance of skipping events in future update someday in the coming weeks, however its not an super easy thing to do. I also plan to implement multi-method sending as I mentioned which will serialize multiple network methods in 1 batch, meaning if you in 1 frame send 100 network calls, they will all arrive at once, no 8m/s, I already have code partially ready for this but still expect ~week before I get that done.
Ah okay, so it sounds like the limitation is more a bandwidth limitation. I imagine it's still actually doing the standard serialization call 5 times per second to send the custom 'events' you're encoding? Just that it uses a fair bit of data...
In either case, I'm super interested in seeing where this goes. A reliable way to send networked events in GENERAL would be a huge step up from what we have right now, and being able to send data with those events is game-changing.
Yea, pretty much, it tries to serialize 8x per second but in reality it's ~about those 5per s and rest gets delayed
The data size since the new version is very minimal for pretty much all events unless your sending arrays with hundreds in length
We can push through UNetwork about 11kb/s standard event with 3-5 parameters has under 50 bytes
Yes, having reliable "Fake TCP" system would be huge, it is the end goal for sure
Oh! I might be getting confused here. In your original post, you said the current system is limited to about 8 individual EVENTS per second. Did you actually mean 8 SERIALIZATIONS per second, each one containing potentially many networked events?
... Or is that not something the system can do (thus you mentioning the eventual addition of 'multi event' sending)?
But first step is gonna be organizing the code bit more, due to some micro-optimizations I'm writing this in a way where both sending and receiving method have around ~1k+ lines which gets bit crazy to navigate though next update is gonna be organizing that a bit, maybe by abusing partial classes or somehri7
8 events, per second, 1 event per serialization and it attempts to serialize 8 times a second
Okay, gotcha.
Oof, yeah. That's a lot of code for VRC to chew on each time it networks...
It's actually optimized when it comes to performance, just not when it comes to working on it
Hahah. Fair.
Wishing you good luck on furthering this project. Good networking is definitely a huge obstacle for world development...
I love figuring out all the ins and outs and challenges of networking, it's fun (until VRChat breaks)
I'm glad someone likes it!
meanwhile back to networking
what is the way of syncing event around 1000 times in span of 3 minutes making them as fast as possible as well making sure not a single of them drops
youre tryin into dmx arent you
hell no
as well as making sure not a single one of them drops
first, you'll need a perfectly reliable network.
nobody's figured that part out yet.
Is there any way to transfer instance ownership to another player?
each object has its own ownership that can be transferred. unless you mean a nickname near instance's info, i dont think that can be changed
Networking.SetOwner(Networking.LocalPlayer, gameObject);
make sure sync mode is not set to None
Is that for an object or the instance?
oh object sorry misread
you cant transfer instance ownership
instance master is always the player with longest time in the instance
Guys, I need help, how to fix the position of the text for VR users? From my computer, everything is fine in terms of position, but when I log in from VR, it doesnāt show me at all correctly.
pretty sure your vr hud must have a world space canvas, then "attach" it to the players hmd via script
Can you be more specific please? It's hard for me to understand without having English skills
your text is part of a canvas right? your canvas can be set to screen space and world space, but i dont think screen space will work in vr, only in desktop
no no, im pretty sure VR requires you to set it to world space
you can then use something like
[SerializeField] Transform cameraView;
VRCPlayerApi localPlayer;
void Start() {
localPlayer = Networking.LocalPlayer;
}
public override void PostLateUpdate() {
VRCPlayerApi.TrackingData headData = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
cameraView.SetPositionAndRotation(headData.position, headData.rotation);
}
and attach a transform to the players face, then attach the canvas as a child object with some forward offset
Damn, I didnāt imagine such a complex concept.
haha sorry, just create a new U# script and add the code, and create a gameobject, add the script and attach the canvas as a child
VRCPlayerApi localPlayer;
void Start() {
localPlayer = Networking.LocalPlayer;
}
public override void PostLateUpdate() {
VRCPlayerApi.TrackingData headData = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
transform.SetPositionAndRotation(headData.position, headData.rotation);
}```
should probably work, untested
https://sun9-19.userapi.com/impg/drETvWE-dIEZoBcjdxZtCb_s5_j-oQIAM88oHw/srW34KJX4v8.jpg?size=475x857&quality=96&sign=f7a7efe81a47bd9604e2783e6f6fcbfc&type=album
https://sun9-35.userapi.com/impg/yPv_I81r-vV6lJvtajtHpzqas5qS5H_tjATh2A/hXfXVQPx57w.jpg?size=463x875&quality=96&sign=a0aa11f32379a1f7a97350a112a375b0&type=album
youre missing the class
sorry i didnt include it
But everyone speaks easily
let me set it up for you
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class aaa : UdonSharpBehaviour
{
VRCPlayerApi localPlayer;
void Start() {
localPlayer = Networking.LocalPlayer;
}
public override void PostLateUpdate() {
VRCPlayerApi.TrackingData headData = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
transform.SetPositionAndRotation(headData.position, headData.rotation);
}
}```
make sure your script name is not just numbers
I just took udon from a Japanese guy, but he didnāt show how it should work for VR
no worries
done
just make sure the file name is the same as class name in this case "aaa"
I understand
you can change it though
What's next?
create a gameobject, add the script and attach the canvas as a child
you might want to move the canvas a little forward, so that the player will look directly at it
you might need to experiement around with it
you can test it in play mode though
š
make sure the canvas is placed infront of the parent, so the players view is pointing towards it
Just need a size 3 times larger?
https://sun9-45.userapi.com/impg/hx99ZMDc6SIuahkfowessTa75gd4EnsvIG2zKA/ybXb2wP1Euw.jpg?size=1110x633&quality=96&sign=8742515af6af03782540106fa884adcc&type=album
https://sun2-21.userapi.com/impg/ovFIQU0-1yPeFDKxNOYpAZNrxN-VdanLV713QA/X7FitCCmqp4.jpg?size=1102x628&quality=96&sign=43a0858010e7dad50ca29892c5baadbb&type=album
test the text being centered to make it easier to check if it works in vr
once you get it set up, you can design it however you like
if you need numbers, you can try this on your canvas
Just in case, Iāll say that I saw 2 of them on the left and right sides of the lenses.
hmm, maybe you changed it back to screen space? the canvas needs to look like this
I don't touch him, everything was the same
can you send a screenshot of your entire canvas in inspector, as well as the image gameobject?
Yes I can, it takes time
https://sun9-26.userapi.com/impg/JSEG8ah_zLro3lw6F9yG9MznDVMqugF_XLQsgw/iQtUCStShlY.jpg?size=1916x670&quality=96&sign=1a82b48a3a8ec0f8a073062496881e06&type=album
https://sun9-78.userapi.com/impg/vCGX7Cn0zYsfaxzRFXSOdZ6ZOzr3m3EuRr1h1g/dum6gD5vmnE.jpg?size=1919x727&quality=96&sign=21c22be9db495d2ee1032990c40adfcd&type=album
https://sun9-70.userapi.com/impg/hBOuY_hVV_f3J34bRn6sbWqpcWS4JgZTOMUfTA/WzsA4H7_GKQ.jpg?size=1919x684&quality=96&sign=fbca41c17cbe0ed72b5ce51589883f98&type=album
https://sun9-18.userapi.com/impg/mErr6lgT4UekBKFLL_9KRGbbEhbkzFaTltA_IQ/CRyyTTIDbew.jpg?size=1919x926&quality=96&sign=81cf8fffb4d23e36571e4f9fd1550ef4&type=album
ah, not sure if you can use canvas scaler and raycast wont work
or well, you probably dont need it
Everything is confusing and it goes on and on
why do you have multiple of these? you only need one
or do you plan to change show different images?
There are about 3 places where there is an anomaly zone
ah okay thats fair
disable collab on the top right
the service is no longer available
unity switched to plastic scm if you require it still
ideally youd only need one canvas and script for the hud and then toggle or change images as you reach another area
maybe try using one for now to see if it works in vr?
and unparent the FollowUI_Simple from it's parent
im guessing i gotta press no?
yeah press no
mkk
you have a weird set up xD
Idk what i did to set it up wrong-
https://www.youtube.com/watch?v=uLi52YrrDmY&list=PLPdWkxUSZ65Fp6ICrU7mIq1znAfPwMhNZ&ab_channel=Iconoclass I been watching this-
EDIT: I now wholeheartedly recommend using World Creator Assistant to set up your VRChat world projects! It's now the way I set up all of mine!
https://youtu.be/F1Tr3Nc9Rxs
https://ask.vrchat.com/t/world-creator-assistant-automate-vrchat-world-project-management/7571
https://github.com/Varneon/WorldCreatorAssistant
Download Unity - https://docs...
to learn how ot make a world
oh wait
im stupid
its a year old-
damn im slow..
are you using vrchat creator companion?
?
ohhhhhhhhhhhhh, damnit, i gotta redo this-
kinda
ish
how do i delete the package-
thing
the manual one
how am i this slow..
dont worry about it for now
just get the creator companion, install it and click create project and select world
thats it! you should be able to follow the tutorial after (maybe)
Sorry for disturbing
But
It's not about work, it works, but I'm talking about something else about the situation, I'll show you an example in VR
sorry im not sure about the issue and i might be going to sleep soon, my best guess would be the canvas is too close to the players face and it ends up rendering the image on both eyes but in the wrong way?
to fix the errors i go up to the collab thing right?
Yes, that is right
I'll try to make it bigger then.
not bigger, but further away from the player view
maybe this is enough
ok
im very confused, you need to find a way to disable collab somehow
maybe turn it on and and turn it off right after?
something like this, and I end up in a strange position
I wouldn't do that, might mess up vrchat sdk
Set x and y of your image transform to 0
Resetting packages does not remove the SDK.
Problems can only arise with the probuilder if something was built on it.
If not, there will be no problems.
im not talking about it removing vrchat sdk, im talking about it messing up the packages that need to be shipped with the sdk
These packages are listed in the SDK dependencies, and they will be installed automatically.
didnt really do anything -,-
still dont know why youd do a hard reset on a fresh project
can you go to your package manager and remove version control? if you need it back, the version is 1.6.0
Then the hub will install several of its packages.
And the user managed to break everything in the packages from the start.
I wrote that you are an idiot. My opinion has not changed
I need help in vc or smth, cause thats so much easier
lmao, you told them to create a new project earlier, i said you are wrong and gave them the answer, you called me an idiot, then proceeds to give my solution right after
what do you gain from being rude and misleading?
Whats happening?-..
Should i reinstall it?
check your console first, does the issue persist?
you need to call RequestSerialization() after changing your status
you dont need to call network events in OnDeserialization
instead just use Activate()
using System;
using UdonSharp;
using UnityEngine;
using UnityEngine.UIElements;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class NewYearsTheme : UdonSharpBehaviour
{
public GameObject[] toggleObjects; // Objects to controll
[UdonSynced]public bool status; // Status of the theme
public Toggle toggle; // the override toggle in menu.
private void Start()
{
DateTime dt = DateTime.Now; //
if (dt.Day < 25 && dt.Month != 12 || dt.Day > 5 && dt.Month != 1) return; // Checks date
status = true; //
RequestSerialization();
OnDeserialization();
}
public void Activate() // Activates/disables theme objects
{
for (int i = 0; i < toggleObjects.Length; i++)
{ toggleObjects[i].SetActive(status); }
}
public void ForceToggle() // Override toggle in world for MASTER
{
if (!Networking.IsMaster) return;
status = toggle.value;
RequestSerialization();
OnDeserialization();
}
public override void OnDeserialization()
{
Activate();
}
}```
network events are meant for things that happen once, and can be forgotten right after, for example playing a sound
Have you guys found that only an owner was updating a udon synced int[]? I have been trying to use RequestSerialization and FieldChangeCallback and itās kind of weirdā¦values are updated but not all of them in the array. I donāt transfer ownership on the class that manages this array, should I be passing that ownership around? Thank you for any help on it as int[] have been very handy.
This is expected, only the owner can request serialization so yes you will have to transfer the ownership if someone else must update and sync the array
Oooooh yah that makes sense⦠thank you for confirming this! I will tweak my code to either send a network event targeting the owner or transfer ownershipā¦hmm may have to transfer ownership since I canāt pass variables around either. Ack!
taking ownership, setting variables, and then sending sync with requestserialization is a very normal process that you shouldn't be afraid of
if i only do SencCustomNetworkEvents. how would i do it with manuall sync?
requestserialization after/before any NetworkEvent functions?
NetworkEvent replace with requestserialization
It is not recommended to combine them.
i should add there is no variable syncing going on
It doesn't matter whether you place it before or after in the code.
In reality, it is unknown whether OnDeserialization or NetworkEvent will work first.
The variable may not change in fact, but the event will work and vice versa.
They cannot be combined.
The result is unpredictable.
SetOwner
You don't need networking for this
Ah thanks
I were just wondering since the tooltips on the udon component are a bit vague
Oh that. Script. I redid it properly. What I made at first was so bad ehe
Sleep deprivation, i blame it on that
My Networking is not working. Im calling my method like this: "SendCustomNetworkEvent(NetworkEventTarget.All, nameof(OpenMainStage));" in a interact, but it does not get synced to other players. Players would have to rejoin to sync again. How would i fix this?
There's several misconceptions here. I could address them all in text, but actually I have a video which pretty directly shows the problem with what you're doing and the correct way to do it instead
This has to be pinned.
^
great vid, was ignoring network cause it made no sense and used some cursed stuff when needed one
now do u#š
How would I go about making a udon graph that when a player enters a trigger area a sound effect plays which doesnt loop?
This doesn't work, what am I doing wrong? I do not understand
you need to use onplayertriggerenter and also branch to only run it if the player it provides to you is local
Iām so lost lol
- use onplayertriggerenter
- onplayertriggerenter gives you a player
- plug that player into a "playerapi.islocal" node
- plug the result of that into a branch
- also plug the flow of the onplayertriggenter into the branch
- plug the "true" side of the branch into the playoneshot
like this?
don't forget to connect the onplayertriggerenter to islocal
ok just did ill test it out
it worked thank you.
i have an animator with one long looped animation. i want to network sync it. my current idea is to send network event every time it loops and event just sets animator bool to true, causing transition to the same animation as other state. So late joiner sees anim1 (unsynced) and on network event instantly transitions to the anim2 roughly the same time as owner, and stays there forever ignoring all the next events. sounds sane? i dont need much precision and i dont mind one-time tearing
At the end of the animation, add an event and run it for everyone. So that the animation starts from the beginning for everyone. You can also completely synchronize the animation through continuous synchronization.
@sweet wolf well yes, animation event -> network event -> trigger animator for everyone
hows continuous sync works in this case, doesnt it just updates variables in behaviour?
also do i need to care about checking the ownership or i can just send custom network event by default and non-owners will just ignore that part
An animation event without transitions. Well, on the graph , instead of an update, any other event can be used. SetOwner , of course, needed to synchronize variables.The load on the network is a little higher, but everything is working right away.
The load on the network is a little higher, but everything is working right away.
oh shi, theres playbackTime? thanks, never seen it, makes things easier i guess... UTC sync also looks great and totally would work.
continuing from [discord.com/channels/189511567539306508/657394772603830360/1176267409749966938]
to rephrase my earlier suggestion: if your players aren't generating many events themselves it would be more network economic to have them take turns syncing a few events as opposed to having a master list of events that is broadcast all at once
that depends entirely on who is generating the events though. You don't really get to just distribute that evenly most of the time, because if every client already has the information they need to "take turns" syncing events... then why do they need to sync it at all?
Continuing from Udon General...
TL;DR. Multiplayer (4p) base defense game. Classes, abilities, unlocks/upgrades. Waves of enemies that approach the base in a MOSTLY deterministic fashion and then attempt to damage your monument. This deterministic behavior can be upset if players apply certain statuses, such as a slowdown or a stun, from their abilities.
I'm trying to come up with a way to
-
locally simulate enemies per client, without using VRCObjectSync, so that players 'see' one another taking actions at accurate positions in space instead of round-trip-delayed last positions of enemies
-
create a system where players can send messages about the actions they're taking and the targets they've hit, when, and with what effects, accurately and reliably to support goal 1
Basically, the 4 players are the ones generating 95% of the events. Outside of player attacks and ability use, there's a small amount of gameplay information from SOME enemies buffing each other, or executing attacks on the players' monument.
... as well as like, non-time-urgent information such as wave setup synchronization and unlocks between waves
but I'm not worried about the non-time-urgent stuff. I'm focusing on in the middle of a wave, where there are enemies moving around and players taking actions, shooting things, using powers
for this i'd have a few objects maintaining modular mostly systems
unless the enemies are randomized mid-wave they shouldn't need much if any synchronization (a list of who shows up where and when at the start would do)
classes and unlocks could be handled by the instance master, since they're data-critical but not necessarily time-critical
hits should be determinable locally with collision bring pretty well syncretized by position, though having a slower back-up sync would ensure consistency
I do not think hits should/can be determined locally.
I would like players to have local authority about if/when/what they hit
Too much frustration could come from a player aiming at one target, thinking they hit it, and then the instance master going "Ah actually on my screen you were slightly off because your pickup didn't sync at the same rate as the event messaging system, so you hit nothing/someone else"
I would do visual effects locally, though
alright, object pool'd weapons can be owned by the wielder and synced with enemies being struck
dunno if enemies being owned by the last one to hit them would be good or bad in terms of network bandwidth
Was intending to like. Have a message say "start a projectile/hitscan here, with this heading/velocity/whatever" and just let it go. The visuals just go where they go, and another message arrives later to say "Okay here's actually who this player thinks they hit"
I was actually thinking of having the enemies effectively have no owner at all and not be networked, and have some game-master object tell players what to do with their local 'copies' of the enemies
oh this is actually quite similar to something I've made. Except it was flying enemies and the players controlled turrets.
The master simulated and synced the enemies invisibly, then displayed their position even to themself back in time by the same amount as the other players are seeing it. As a result, everybody saw the enemies in the same place at the same time, even the master.
Then the guns have a looping circular buffer of events with relative timestamps stating how far in the past each buffer location was shot. When the local player shoots, the bullet just travels normally. But when a remote player receives that shot event, it takes the current time and compares it to the time the bullet was actually shot, and speeds the bullet up. Every frame, it simulates an extra fraction of a second of "pre-simulation" time, which speeds the bullet up in order to catch up to the "real" position faster. As a result, once the bullet has consumed all of it's pre-simulation time, everybody sees the bullet at the same location at the same time.
When both the enemies are synced up in time, and the bullets are synced up in time, everybody sees the bullets hit the enemies at the exact same time at the exact same location. This was actually close enough that under good network conditions, I didn't even need to sync health. However in order to make it more robust under bad conditions and also for late joiners, it does sync the health of the enemies.
Woof. That sounds really involved. It also sounds like you're optimistic enough about how robust the system simulates/lock-steps that you (mostly) don't need an authoritative "Okay here's what actually happened" event, or in other terms, a split between visuals and actual gameplay
also, the players are on a moving ship so there's additional latency compensation and timestamps added for where the ship was when the player made the shot
I wonder how you decide how far back in time to simulate the enemies? Considering each different player has a different simulation time delta, and that simulation time delta is fluctuating constantly
OH fuck, moving ship. ā¤ļø
This is reminding me of my airship game idea from earlier this year. That wound up being too much to bite off on my first game world attempt. Maybe I'll come back to that some day.
it's actually entirely arbitrary, but as long as you apply that same number to everything, it works
at least it is for the owner of the enemies
for the remote players, it's networking.simulationtime
Oh, so you just pick an amount of time that is hopefully always enough for any amount of simulation time lag for the master
and then on remote clients, use simulation time to lockstep the enemy movements?
and I use simulationtime of the player instead of the objects so that it is more stable and consistent, as well as synced between all objects that player owns
yep yep
Hm. I can't immediately intuit the easing/rubber-banding logic needed to keep that lockstep, since the simulation time fluctuates
but I can imagine it abstractly
the bullet catch-up part?
Well, the enemy movements
oh, that's just another circular buffer of positions, and linearly interpolated between them based on simulation time
hmmmm
Okay. Still seeing it abstractly. I understand, but I just can't grok it in my head without sitting down and looking at it all work š
could make it look nicer with a non-linear interpolation like objectsync does but meh
to be fair I made this 4 months ago and would have a hard time following everything too now 
it was a manic couple of weeks
It sounds like, one way or another, I'll have to do SOMETHING vaguely like that. I wonder how much of a difference there is between having the enemies be master-owned, vs everyone owning their own local versions and 'talking' back and forth to make corrections or compensate for "Hey player 3 just hit this group of enemies with a stun so roll them back this amount of time"
the implementation is complex, the theory is simple. I still remember the theory at least
Also, this isn't mission-critical RIGHT now, but I am also trying to keep it in the back of my head that I want there to be some contingency logic to prevent the ENTIRE game from bricking if a player, or god forbid the instance master, disconnects
It's such a pain to be playing an hour or two-hour game and then lose everything because VRC or SteamVR decided to go "Ah, nvm!"
yeah I mean that's why you just don't store too much entirely locally on one client. Keep everything in sync and it can be seamless
Ownership and synchronization has to be able to be transferred safely
yeah
that's one of my big concerns about having most of the game logic be "Okay instance master controls this, instance master controls that"
Having everyone have the state and talk back and forth feels... safer. But harder š
also something that will be difficult is handling any kind of scenario where the enemy's movement gets changed by the shot. I'm concerned about this because at the moment, the enemies move in a lockstep delayed manner. If the master sees a bullet hit and as a result changes some state in the AI to make it do something different, that will have a built-in delay which doesn't feel great. I'm already slightly cheating in this way when it comes to the final destruction. The enemy gets disabled on the last shot even before you have received the synced health confirmation from the master
and I think making the enemies dynamic and reacting to hits in this way is very good game design ⢠so I'm gonna have to figure it out somehow
In my game, enemies will not 'change behavior' when being hit, but the RESULTS of their otherwise deterministic actions can.
For example, stunning an enemy will cause it to pause on its path, and it will also have to roll-back any attacks the enemy has made in the 'lag' time between players.
Also if I slow an enemy that can buff other nearby enemies, the available "nearby targets" for its buff will change since it slowed down and other enemies nearby kept going at full speed.
mhm
Also, no pressure/obligation, but if this would be interesting to like, hop on a call, dig into some code, make some test cases for at some point, I'd vibe with that. Regardless, I appreciate your time chewing through this with me.
I should really get back to work š
Yeah, of course š
I still have a lot to think about, but your thoughts on manually time-delaying enemies for everyone, and the mention of buffer arrays or stacks and "acknowledgement" messages for resizing, have given me some direction to explore at least.
Currently unemployed myself, unfortunate layoffs back in June. x.x But I'm central time.
I'm not sure why you would want that though? Also, if you were doing a master verification system you would probably want to use snapshots with timestamps for everything.
You're not sure why I'd want... players to aim at something, think they're gonna hit, and then miss because the master is authoritative on everything?
... I don't! x'D
I'm very sure you wouldn't want that lmao
I explicitly want enemys to be locally authoritative so that that DOESN'T happen, and players get successful hits when it looks like they got successful hits locally.
Ye, that's how zombie survival does it.
I can't find it said explicitly in the docs but does VRCObjectSync handle late joiners on it's own?
pretty sure it does but wanted to be safe
Yes
sweet ty
So i made a syncd toggle script that works and I then added a portion where after the part confirmed working finishes set the bool value of a animator to true. However, when I test it. The value change and works for the one who clicked but no one else. (When the script was already setup to work globally and still works) its like the animator is denying the change from the udon in play for others.
Here is the setbool portion i added
Here is me setting the value and syncing it
I have even done it this way manually with no sync since a syncd variable leads to this anyway and still nothing
this is to show that a sync variable is already leading all players to the right event. But the animator still rejects changes
So for both players to be here means the sync toggle prior to the set bool worked. This also shows how the left pressed the button sees things moving. On the other hand the right does not and animator isnt firing.
So for anyone else that has similar problem. I was unable to find a suitable fix for this. You essentially have to work around unity being weird and parent your animator on a toggle that is not a child to to a disable/enable object.
can anyone explain why vrc station made avatar broken everytime sit on it?
Im kind of new to networking. How would i properly sync the colliderenabled bool? In my current code the OnPlayerJoined doesnt sync it properly. Late joiners wont get the updated variable
- Use Manual sync instead of Continous.
- There is absolutely no guarantee that the synced variables are up to date when
OnPlayerJoinedis called. Please overrideOnDeserializationinstead. - Never use Network Events to sync state.
When setting a synced variable:
- Take ownership
Networking.SetOwner(Networking.LocalPlayer, gameObject); - Set the variable(s)
colliderEnabled = !colliderEnabled; - Call
RequestSerialization();to mark that VRC needs to sync the variables.
Also, I'm not quite sure you even need sync for this? It's a bit hard to tell what you actually want this to do.
I have a collider in my club world so that players can not enter the DJ booth when itās turned on. I have a list of people who can enter the booth freely (isDJ) but I also want to be able to turn off the collider manually.
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class LockBooth : UdonSharpBehaviour
{
public DJBoothMaster djScript;
public Collider boothDoor;
[UdonSynced] private bool colliderEnabled = true;
public override void Interact()
{
// Please just update your djScript to either take no params or a VRCPlayerApi
if (!djScript.isDJ()) return;
Networking.SetOwner(Networking.LocalPlayer, gameObject);
colliderEnabled = !colliderEnabled;
RequestSerialization();
UpdateCollider();
}
public override void OnDeserialization() => UpdateCollider();
private void UpdateCollider() => boothDoor.enabled = colliderEnabled;
}
There are probably syntax errors since I wrote this directly in discord
You have examples in your project. in the Udon directory.
Look at everything and do what you need.
The master is the player who has been in the instance the longest, and is no different from others.
There is no sending queue. instance master store doesn't exist either.
Whoever is the owner of the object is the current server for the rest.
Multiplayer experiences are the heart of VRChat, so creating a world that reacts to players and synchronizes the data between them is key.
Im working on a UdonGraph that makes it so when a couple of people are in a specific area when this Trigger is enabled or turned on where all the players are, It will teleport all the players to a specific area but only one player by random will be teleported to another area but all the other players go to the same spot except one. i have the teleporting trigger down, i just need help with the teleporting one by random to another location while the others go to the same spot. Could someone show me an example udon by chance please?
does anyone know if late joiners have OnDeserialization() called when they join? I'm needing to make my own object pool system and wondering if just having this in OnDeserialization() is enough for late joiners.
for (int i = 0; i < gameObjects.Length; i++)
{
gameObjects[i].SetActive(gameObjectStates[i]);
}
yes that's exactly what you need to do. Late joiners will receive OnDeserialization containing the latest state soon after they join
While a player in my world holds an item, the gravity should be superlow for them. But on drop it should be reset. But when the player drops the item, the gravity is low forever. How can I fix that? x_x
Hi there, im having troubles while syncing a string array. I have this code that works fine with int[] but when i do it with string[] it doesnt work, i would appreciate any help.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class SyncTest : UdonSharpBehaviour
{
[UdonSynced, FieldChangeCallback(nameof(TestArr))]
internal int[] _testArr = new int[10];
public int[] TestArr
{
set => _testArr = value;
get => _testArr;
}
public override void Interact()
{
if (!Networking.IsOwner(Networking.LocalPlayer, gameObject))
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
}
int[] test = TestArr;
test[0] = 1;
TestArr = test;
RequestSerialization();
OnDeserialization();
}
public override void OnDeserialization()
{
Debug.Log($"Message Received: value 0: {TestArr[0]}");
}
}```
it will fail to sync if any synced strings are null, including strings inside arrays. Make sure to set all the entries inside the string array to string.empty so they're not null
yes, ive been told this before and made start function set everything to empty, but it still doesnt fire the fieldchangecallback for the remote players
it does sync and fire it for local player tho
i did this
FieldChangeCallback only happens on remote users if the size of the array changes. Reaasigning it back to itself will not count as a change
Use Ondeserialization to respond to when you receive data instead of fieldchangecallback
Properties are great for controlling variable access as intended, but I would not recommend using them for sync, especially with arrays
i see, thanx you!
what exactly GetPlayerId is getting? Is it some constant vrchat id or just instance number that changes every world?
the playerid is a number that is assigned to each player when they join the instance. The first player to join gets 1, and every player to join adds one to the number. Numbers never go back down or get reused
Got a question~
So in UdonGraph you usually call RequestSerialization(); and the owner that requested will ALSO call the Variable Change Method. I usually put all the necessary logic that is affected by the variable in the Variable Change.
I recently got into U# and the owner is not automatically calling OnDeserialization(); when requesting Serialization.
Is it a bad idea to call it manually then?
Soo it makes sense to me, so I don't have to have multiple method calls. Example, if I set a Bool of an animator to true, that'd make more sense to do it once instead of repeating that line twice. I rather have one spot where I do all the things when a variable changes.
you definitely don't want to call them manually, U# will call them for you when those events happen
the owner also doesn't get OnDeserization(), that is the event that gets called when a remote player receives new data over the network
if you want to know if serialization has finished on the owner's side you want to override OnPostSerialization(SerializationResult result)
Im trying to test my networking and syncing on my world but I cant do offline testing with multiple clients
I get this error and only one client opens
I feel like this would be a common problem but I couldnt find anything on it
Specify the path to vrchat.exe in the SDK settings
so if you where to change an animator parameter for both local and remote, you would write the same line twice? doesn't make much sense to me.
kinda? you would do something like this
string myParam = "foo";
[UdonSynced] float myValue = 0f;
Animator animator;
void MyFunction()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
myValue = 1f;
RequestSerialization();
#if UNITY_EDITOR
SetParameters();
#endif
}
void SetParameters()
{
animator.SetFloat(myParam, myValue);
}
public override void OnDeserialization()
{
SetParameters();
}
public override void OnPostSerialization(SerializationResult result)
{
if (result.success)
{
SetParameters();
}
}
at least that's the safest way to do it, in reality you can also just do this and it should be fine too
string myParam = "foo";
[UdonSynced] float myValue = 0f;
Animator animator;
void MyFunction()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
myValue = 1f;
RequestSerialization();
SetParameters();
}
void SetParameters()
{
animator.SetFloat(myParam, myValue);
}
public override void OnDeserialization()
{
SetParameters();
}
@iron burrow sorry I had to make some edits, does that code make sense?
yee def makes sense. I still think its okay to do both until someone says nooooo using OnDeserialization for the owner too is bad xd
well the owner doesn't receive OnDeserialization() that's why we need to call SetParameters() either in OnPostSerialization or when you request serialization, and you shouldn't call OnDeserialization() manually
yeah but why can't I just call OnDeserialization() my own then
if theres no downside to it. If there is am more than happy to know about it xd
I don't think it will break anything on it's own but it's just bad practice
and you need to call a function anyway, might as well make it SetParameters(), it's just much cleaner
but what if i need to call like 10 functions after I make one variable change- am not gonna type 20 lines for that xd
then it sounds like you need to refactor you code lol
I could create a new function called smth on variable change and have the 10 in there~ that would work xd
ya I was about to say that, or you could make one big function that does everything you need
and I mean this is basically what you were planning on using OnDeserialization() for anyway
yee~ I just wish there was more documentation on network related stuff and good practices
thank u for ur time!
npnp! general rule of thumb is you shouldn't ever really need to call virtual methods, especially in udon. Udon should handle everything for you
Sooo why is can't I change the value of a Data List to int? I always the error that I'm attempting to access Double token as int.
Documentation says that I can do that with .Int tho?
playerManager[1].Int is not working for me although it's 100% an int
Any ideas? <-<
if you've parsed from json, all numbers are doubles. If you try to get an int from something that is a double, it'll crash. the solution is to get the double out instead, and if you want it as an int, round or cast to an int
alright I'll always assume its a double now and just cast it then
seemed to work! thank you.
hey if I request serialization on a game object is OnPostSerialization guaranteed to get called even if no new data was serialized (I didn't change any synced variables)?
well, whether or not you changed anything doesn't matter at all. It will re-serialize all the same
sweet ty
Silly 6am question, is networking limited to 11 kilobits or kilobytes per second?
kilobytes - and note this is a very soft limit, spikes can occur but result in reduced speed afterward
Oh hurray then! thank you š
for reference i was looking into pedestrian and ai traffic systems. thinking about the networking side of things
I'm pretty sure the max you can serialize at a given time using manual sync is like 49 kilobytes too if you need the extra data
That's size of data chunk. The send out rate is still 11KB/s. And the rate is shared with all the thing in the scene, including avatar data.
Hm I didn't know that, the more you know
Does OnPlayerLeft always trigger before OnOwnershipTransfer, in case the ownership gets transfered?
OnOwnershipTransfer always triggers after OnPlayerLeft.
Anyone have any idea what Networking.IsObjectReady(gameObject) is for? The docs just state "Returns if the object is ready," but I'm unsure what exactly that means. It returns true in Start (for both Owners and non-Owners) and OnDeserialization. Is it just some old method that shouldn't be used anymore or am I just using it wrong?
might help?
Hmm, I though instantiated objects couldn't be synced though. I tried instantiating a networked object (UdonBehaviour with Manual syncing) and IsObjectReady still returned true for that object despite it not being able to be synced. Not sure what it does
This smells like chatgpt
Most likely this is to test objects if they are ready to receive and send networking data like it's "ready for the network". This might return false in OnPlayerJoin for the local player and during some other events that can happen before Start. This is probably an old function that's not necessary for regular use though I'm sure someone will find a use case. With Instantiated objects they can send out and receive data however you'd need to match the network ids between clients which isn't possible afaik. Might relate with Networking.Instantiate(gameObject) but idk, go test it!
would this be synced for late joiners?
no
how do i sync the animator's bool for late joiners in that case?
and to connect the sound parts of the graph, would i add a block after the āSet boolā?
After Bool Change, add block
how would i apply that same sorta late-joiner sync for this RNG variable?
networking is where i'm least knowledgeable in udon
What if you changed the SendCustomNetworkEvent to just SendCustomEvent so it's just the owner (remote players don't set RNG). Then on RNG Change (or in both the 'tp' event (for the owner) & OnDeserailization (for non-owners), run some event to react & update whatever depends on the value of RNG. That way the Owner decides what the RNG value is (if everyone ran Random, it would be different for each player), and also supports late join as whatever depends on that value is updated as soon as it's received. Also, I think the 'RNG -> Set RNG' is redundant, as synced variables can only be changed by the Owner. (unless that's doing something else, I'm not too familiar with UdonGraph specifically). RNG would also need to be a 'synced' variable if it isn't already. Hope this helps, if you need anything clarified let me know ^^
Something like this I think would work (assuming I'm understanding correctly) (note that RNG's 'oldValue' may be inaccurate if you're a late-joiner):
alright, that seems like itād work
Hey everyone, I've been working on an Amplify area script using OnTriggerEnter, but I'm facing some challenges, especially when it comes to syncing with late joiners. Has anyone successfully implemented this before? I could use some advice or maybe even a script example that has worked well for you. Your insights would be greatly appreciated!
Add several variables and keep them in sync.
I did but I keep falling in creating even an array of the amplefied players. And just reamplefy them ondeserialization
save the player ID to a variable and synchronize it.
If its. One player not list of players I have to move throw
I'm trying to create a basic system in which remote-players click a button to make a request to join a party slot. The requests are vetted by a manager object that the master-player owns, and if verified, the manager object assigns ownership of a party-slot object to the remote-player.
It's proving difficult to come up with a solution to this that doesn't suck?
Attempted solutions:
-
Remote players blindly attempt to seize ownership of the party-slot object. This is unideal because the remote player sees themselves briefly as the owner before the master returns "false" for the ownership request. It also creates issues when the master player leaves and the world state needs to be juggled/repaired.
-
Remote players call a networked event on the manager object to make the request. This doesn't work because networked events carry no data or signature that the specific remote player called it, so the manager object doesn't know what player wanted the party slot.
-
Remote players press a button and gain ownership over the button. Then, all clients - but most importantly, the master-player - sees OnOwnershipTransferred() fire, and call a function on the manager object to request a party slot. Non-master-players return out of the function instead of fulfilling the request, and the master-player runs the request function as normal. This doesn't work because OnOwnershipTransferred() fires when the button returns to the master player after the remote player leaves the game, resulting in the master player eroneously 'taking over' the last slot the leaving player was in.
-
Same as above, but the button uses OnDeserialization() to call the function on the manager object, instead of OnOwnershipTransferred(). This still doesn't work, because OnDeserialization() is called when players join, resulting in new joins erroneously asking for a slot (all slots) immediately upon joining the world.
...
5) Be really obnoxious. When a player clicks the button, they take ownership, set a synced bool "ButtonPressed," and then request serialization. On all clients' local OnDeserialization() event (but again, only really important that the master player sees this happen), if ButtonPressed is true, make the request call to the manager object. Then, have the master re-seize control of the button immediately after, to reset ButtonPressed to false. This seems wildly inefficient? Like, I don't think it TECHNICALLY breaks anything. There's a possible edge-case where someone joins RIGHT when ButtonPressed is true, before the master resets it. But since the late-joiner isn't the master, it doesn't result in any unwanted behavior anyways. I just feel like there has to be a less obnoxious, more streamlined way to do this...
I haven't quite figured out what exactly is involved in requesting a party slot but I don't think it is much different than assigning a pooled object (which I'm doing). I'll suggest that a player not take ownership of anything related to administering the slots. My object that allots objects isn't even network aware. The game objects it spawns aren't either those they each contain a child object that performs manual syncing. So maybe you can "hand out" a "party slot" when requested by a player and they can interact with it.
It basically is pooled object assignment, except it's manual (players can choose how or when to join or leave, and also there are only a limited amount, instead of 1 for each possible player in the world).
I definitely don't want players to take ownership of the management object that's doing slot assignment. But it is 'technically' network aware, because only the master gets to run its important functions, and it calls ownership assignment on the 'pool' objects when it assigns or un-assigns them. I also do some syncing of the list of slotted players, so that other players have a consensus on 'who' is in 'what' slot in one central location.
The addition of the 'unrelated button whose only purpose is to send a request function to the manager object' was so that no ownership juggling had to occur, of the manager object itself.
... but now ownership juggling occurs on the request buttons instead =u=;
The buttons don't care about ownership except to fire the click event. The system I mentioned apportions "flight sticks" which could just as easily be a ticket to a party. Once assigned all the actions made on the flight stick are networked. So appearing/disappearing, movement, changing color when clicked, etc. Perhaps something grabbable would work for you. So long as the player is holding onto it they are in control of it. In theory they can select options, on it, confirm the invite, etc.
I certainly don't WANT the buttons to care about ownership.
I got to that point trying to find a way for an arbitrary player to send a message to the instance master with their VRCPlayerAPI associated with the message.
Unsure I want to require a player to grab something in order to click a button on it. Just thinking about basic interface design, most worlds with 'world options' tablets don't require you to hold them to operate them, and most players don't want to hold them to operate them either.
I (think) you could send a message to the instance master (if you mean master) or owner if you mean owner by simply syncing the value and letting the method check what the status of the LocalPlayer is. I do that a lot both for who can interact with something and who should react to that interaction.
Well think about it a bit before you discount the idea. You aren't creating "most worlds" and this isn't a world option. They need to click on a button located in the world right? They could just as easily pick something up or be given something when they click on the button. They don't need to hold onto it forever. Anybody can drop one of my flight sticks anytime they want.
That's basically what I'm doing right now. The person that clicks the button assumes ownership (of just the button's script, which is NOT the manager script), changes a bool, and then syncs. Then all clients run deserialization, and if the bool is true (it was clicked), attempt to run the request function on the manager script with the button owner's VRCPlayerAPI.
The manager script then discards all function calls not made by the instance master, and runs the logic to determine slot join/leave status if they are the master. And also then resets the button sync variable to false so it can be clicked again.
I honestly want to get rid of the synced bool entirely because I'm worried about the possibility of it accidentally getting stuck in the TRUE state if interactions and network traffic resolves in exactly the wrong time
But udon behaviors won't even run OnDeserialization() if there's no synced variables to sync
Maybe I don't care about the state of that boolean at all and just use it as a way to assure OnDeserialization() fires...
It's dumb but it'd work I think š¤
That's a problem but it can be solved with either a timestamp or sometimes an enum. You can get 3 state logic with an enum.
Again I wouldn't tend to label this odd or unforeseen. The client calls OnDeserialization to let listeners know that values have changed. If they don't change we wouldn't want it to call OnDeserialization. That could have side effects as well.
I can rationalize in my head that it 'makes sense' yes.
Some of the behaviors and edge-cases I've seen with Udon make more and less sense than others... which makes it a little hard to predict for sure what's going to happen before I run into it face first š
For example: I haven't tested yet whether or not OnDeserialization() will fire if there ARE synced variables in the object, but their values haven't changed yet. Based on OnDeserialization() not firing when there just aren't synced variables at all, I'd assume not. But I don't know yet.
I was under the impression that if the values did not change they would not be sync'd which makes sense for automatic sync'ing (they are sync'd already). OnDeserialization will fire on a new joiner however as they would have only just received the values. Reading the docs I was made aware of: OnOwnershipTransferred(VRCPlayerApi player) which you may be able to use. One would know which player just became the owner.
I would suggest (if you haven't done so) to create a world that is just used for all sorts of testing. No need to mess with a world that you are relying on.
using onownershiptransferred had problems when players left and ownership returned to the master (though the master didn't click the buttons)
and yes, i've got a test world i'm doing all this scrappy testing in.
In either case, I did get the slot thing working. Just finished testing all the weird edge-cases with users leaving at different times. Hopefully it's robust, I guess we'll find out x'D
Let's say I have an object that's owned by player 2/3/4 etc., and I need it to tell an object owned by player 1 to send a custom event to all, what's the best way to go about that?
That's not quite what I mean; An Owned object, needs to tell an object that's not owned by the same player to send an event to all, I don't think I can transfer ownership of the second object because it would be passed around too frequently for what I'm doing
Perhaps so.
But the objects themselves do not send or receive anything. You can only check the owner, and the player can do something.
Alright, I have an apparent gap in my understanding of udon networking, and don't know what I'm missing.
I ran a test with two players. In this test, player 1 clicks a button, requests serialization, and OnPreSerialization, starts a clock. Player 2 receives this message and calculates the amount of time the message took to receive them using the OnDeserialization result (result.receiveTime - result.sendTime). They then start their own clock, starting at this 'offset.' Both players now have a running clock, and I PRESUMED that these clocks would be 'in sync' if you corrected for the time it takes to send messages back and forth.
To test this, I then let each player send one another messages containing their local clock time. When each of these messages are sent, the clock time is recorded OnPreSerialization (to account for the variable timing between serialization events). The receiving player then compares their local clock with the received 'timestamp' (once again corrected using result.receiveTime - result.sendTime).
When the player that started the clock (we'll say P1) sends a timestamp, the other player (P2) gets an 'error' between their local clock and the corrected timestamp of about plus or minus 1ms or less. Acceptable.
However, when the player that DIDN'T start the clock (P2) sends a timestamp, the P1 gets an error of about negative 130-150ms, indicating that P1's clock is about 150ms "behind" P2's clock.
Out of curiosity, I measured what the serialization delay (result.receiveTime - result.sendTime) was at the original clock-start event and noticed that this original offset was about 100 to 160ms, but DIDN'T regularly correlate with the negative 130-150ms of 'error' calculated when the timestamps were sent.
... What's going on here? What am I missing?
I had a hypothesis that simulation time was the missing link here. I'm not sure that's true because the docs say that simulation time is used for **players **and **VRCObjectSync **- NOT manually synchronized objects. Which is what this is. But I tried the following anyways:
- When a player sends their current running clock time, they SUBTRACT their 'simulation time delta' from the clock (I calculate this as Time.RealTimeSinceStartup-SimulationTime)
- When a player starts their clock using this received information, they add back on THEIR simulation time delta (calculated in the same way, but from their reference point), in addition to the time taken for the message to arrive (receiveTime - sendTime).
While running two local clients, I noticed that this results in the two clocks being almost perfectly in sync about 95% of the time, as long as I don't bungle a window and freeze the game momentarily.
However! My 'error' checking math - which I adjusted to take simulation time into account - still shows a frustrating asymmetry. When the clock-starter is the one sending the timestamp check, the second player sees 1ms or less of 'discrepancy,' but when the second player sends the timestamp check, the clock-starter sees 130-150ms of 'discrepancy.'
This now feels like I have a dumb math error...? But I can't identify it.
Either that or I just fundamentally misunderstand how time is or isn't "synchronized" across clients in multiplayer games. Which is also possible, I guess.
Here's the math:
local OnPreSerialization():
Start clock at 0
float simTimeDelta = realTimeSinceStartup - simulationTime(gameObject)
syncedTimestamp = localClock (which is 0) - simTimeDelta
remote OnDeserialization():
float simTimeDelta = realTimeSinceStartup - simulationTime(gameObject)
float deserializationTime = result.receiveTime - result.sendTime
Start clock at syncedTimestamp + deserializationTime + simTimeDelta```
local OnPreSerialization():
float simTimeDelta = realTimeSinceStartup - simulationTime(gameObject)
syncedTimestamp = localClock - simTimeDelta
remote OnDeserialization():
float simTimeDelta = realTimeSinceStartup - simulationTime(gameObject)
float deserializationTime = result.receiveTime - result.sendTime
float error = (localClock - simTimeDelta) - (syncedTimeStamp + deserializationTime)
// When clock starter sends a timestamp check, player 2 measures an error of about 0.001
// When player 2 sends a timestamp check, the clock starter measures an error of about -0.130 to -0.150```
God, I really wish there was thorough documentation and examples for synchronizing clients in time.
Simulation time was a red herring and actually introduced new problems. Ultimately removed the simulation time adjustments from the sync and check timestamp functions.
Turns out, something about send and receive times gets fucky when you test locally with multiple clients. This clock-sync-and-timestamp-check system works as expected with very small ~1ms error when testing with other actual players over a real internet connection.
I... do not know how to feel about that. It means there's a lot of shit that I cannot test locally at all because time-sync will just entirely break.
Same gripe here. Never resolved
I went back to using time = cachedAtStartGetServerTimeInMilliseconds + (Time.realtimeSinceStartup - cachedAtStartRealtimeSinceStartup) for the send and receive times, however, Networking.GetServerTimeInMilliseconds() can be negative for some clients.
For my case, why not just use simulationTime? I wanted a "raw" enough variable for latency between two clients so that I could implement my own non-linear networking interpolation.
R = simulationTIme, A = R * 0.5 (lol), C = the method I mentioned above which seemed good enough until the server time goes negative
Not arguing here but what is the issue people are trying to solve? Multiplayer games across disparate networks, hardware, operating systems, et. al. is inherently problematic. https://gafferongames.com/post/deterministic_lockstep/
Introduction Hi, Iām Glenn Fiedler and welcome to Networked Physics.
In the previous article we explored the physics simulation weāre going to network in this article series. In this article specifically, weāre going to network this physics simulation using deterministic lockstep.
Deterministic lockstep is a method of networking a system from on...
oh wow gaffer on games, that just unearthed an ancient memory, i read this https://gafferongames.com/post/fix_your_timestep/ like 15 years ago
but i will avoid going offtopic :p
Here are some of my ... plausible understandings, from the tests I ran:
-
Server time can be negative, and it can roll over from positive to negative in the middle of a session, and that's weird. It may be a good source of truth to use, but I found some unusual inconsistencies with it last time I did this kind of testing and so right now I avoid using it until I can get around to running a better test.
-
Time.RealTimeSinceStartup is how long since you started the VRChat application.
-
Deserialization result appears to give you an estimate for when the serialization was sent, but already adjusted to be from the frame of reference of YOUR LOCAL RealTimeSinceStartup - and the RealTimeSinceStartup when you received the serialization.
-
When running multiple local clients for testing, something about the OnDeserialization result is foobar. I don't know what though. Nothing LOOKS wrong. Just looking at the raw numbers, everything seems right, but as I discussed in the gigantic wall of text above, I was getting weird results where the two local clients were getting asymmetric 'errors' when comparing half-ping-corrected timestamps to their local synced clock.
-
Simulation time, like deserialization result sendTime, is a timestamp that exists in the frame of reference of YOUR LOCAL RealTimeSinceStartup. However, it's some amount of milliseconds back in time (for example, if your RealTimeSinceStartup is 1783.953 seconds, you might see another player's SimulationTime is 1783.643 seconds), and that 'delta' changes frequently based on what the game thinks is needed to smoothly interpolate a networked object. This is used for players and objectsync, and may be useful for custom, frequently-updating interpolation, but in my use case, it introduced timing/error artifacts that I didn't know how to account for. I get the impression that for most manually synced objects, simulation time is not a good thing to use.
...
An example of an issue I was having with simulation time:
When I sent a timestamp, I would remove what the sending player thought their simulation time (for the OBJECT sending the message) was from the timestamp.
When a remote player received the timestamp, I would add back on what THEY thought THEIR local simulation time (for the object) was, to the timestamp.
When serialization events were spammed, the receiving player's simulation time for the object would slide further back in time as part of whatever its 'mechanism' for compensation was. But because either A) the serialization events weren't ACTUALLY coming any later, or B) because receiveTime - sendTime already accounted for the serialization events coming later, this extra delay on the simulation time caused the timestamp comparison to become more and more incorrect the more often serialization events occurred.
...
Another issue I had was that, very reliably, the FIRST time I sent a 'start the synced clock' message (in a given world load), the sender or receiver's simulation time would be non-nominal, resulting in about 60-80ms of extra error in timestamp comparisons. I know this error was in the original synced clock start event, because all timestamp comparisons after that carried this error.
However, when I stopped and restarted the clock, this 60-80ms of extra error would vanish, and go back to about 1ms or less for the rest of the play session.
Removing the simulation time math from the timestamp comparisons all together solved this one-time anomaly.
At a high level, I'm just trying to learn how to take time-sensitive networked messages and correctly 'line them up' in time on the receiving client, both in general, and specifically using VRChat's framework. Even if I had a general understanding of network design, there are/were certain aspects of VRChat's implementation - like the player-to-player-ish model, or the specifics of what Deserialization result does or doesn't give you, or how the heck to properly use simulation time - that I don't fully understand.
I'm working on a co-op monument defense game where 4 players shoot a bunch of moderately slow-moving enemies with a variety of weapons and abilities. Some hitscan, others projectile, etc etc etc.
When a player shoots an enemy or uses an ability, they need to send some kind of networked message to other players that they did so, along with a bunch of related information about what or where they shot, what effects they thought were active, etc. Unrelated to this current discussions, I'm experimenting doing that using a message system that syncs a size-dynamic array of bytes that encodes all of this information in tokens.
When a player receives that message, they need to know when in time that event actually happened, for multiple reasons:
- If the message was "The wave started!" they need to 'catch up' with the client that started the wave, so that both players see enemies in the same place
- If the message was "I shot a projectile!" they need to know how much to catch-up the projectile so that it hits at about the same time and place that the sending client thought it hit.
- If the message was "I killed this enemy!" they need to know if the enemy was killed before or after the enemy took certain actions, like buffing nearby enemies or attacking the central monument, so that they can 'roll back' those actions and maintain visual parity/synchronicity in the game state
- (and more...)
...
And yeah, of course multiplayer networking is inherently problematic. But it's been problematic for decades, and yet we still have multiplayer networked games, so people have come up with lots and lots of different solutions for things in lots and lots of different situations and environments.
I, unfortunately, don't have decades of experience as a multiplayer network engineer under my belt, and so I'm trying to learn some of that shit for the first time.
Folks this is before implementing any kind of network representation such as lockstep, extrapolation, etc.
I just wished there was a less padded simulationTime so that we can implement our own interpolation. It frustrates me that we have to implement our delta of send and receive time just to have it be lower than the observed ~1000ms in the screenshot above.
Latency difference between object sync (textured) and manual sync (red)
One of the biggest benefits of discussing things can be to arrive at a suitable working resolution. Maybe even more than one depending upon the level of fidelity one needs. VRC could benefit from a "well-known" public repo of solutions for the types of things typically encountered. There are a few (perhaps more than I know) but I don't know how well they are maintained.
As for syncing time across players, it might be a good idea to produce a shared library. That way there is a baseline and something that other devs can review and perhaps improve upon. The library would get better over time as people used it. If we use C# or Node as examples there are libraries for everything.
Correct me if I'm wrong, but shouldn't it work if you just do time = Networking.GetServerTimeInMilliseconds() - cachedServerTime? Then the master syncs the cachedServerTime to everyone else, so they can do the same.
The problem is that the server time can roll over from a really large number to a really large negative number.
Oh yeah, I thought it would be fine for some reason. Maybe there is a way to circumvent or minimize the issue?
Resisting the urge to suggest Math.abs().
But in seriousness I left it there to go work on other things and currently not aware of a better way.
I was thinking to have it branch to simulationTime() if it ever goes negative
Okay, so I got a bit suspicious and tested it. Getting the delta between two server times seems to work fine as long as the delta is not bigger than half of a the 32 bits. That's like 24 days, which I think is more than fine. Again, correct me if I'm wrong though š
It sounds like one would want to use Epoch Time representations so there is no rollover to deal with
2,147,483,647 + 1 is still -2,147,483,648
long 64-bit signed integer -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
same diff
We are talking about -2,147,483,648 - 2,147,483,647 though, which is 1

So we know that 1 millisecond passed in this case, which we can use for all sorts of things, especially if everyone has the start time synced. Alterntively, you could always sync the current server time of the action and always be in the clear.
If you save the timestamp before the rollover, and then try to get the delta after the rollover, you get the wrong answer.
You need to detect and handle the rollover
there is no way around this
But isn't it literally this: -2,147,483,648 - 2,147,483,647 = 1
We get the right value for the delta, which is 1
My calculator says -4,294,967,295
Which makes perfect sense
Oh god
since you have a really small number, and are removing from it.
I think I get it now. The delta also overflows, but ends up right in the end lol

I was just purely testing the values in code but that fact completely escaped me.
I wonder if Networking.GetServerDeltaTime() accounts for server clock integer rollover...
I don't think that's a method.
Also, since I see it's not that easy to read from my previous comments:
You can simply time = Networking.GetServerTimeInMilliseconds() - cachedServerTime and it will rollover properly!
Calculates the difference between two server time stamps as returned by GetServerTimeInSeconds()
Ahh, I guess that's a thing. I never had to use it. Maybe it's useful, but hard to know since that's how it looks like internally:
i'm not really sure why this isn't synced between players
(bought is a synced bool)
You need to call RequestSerialization, after changing the value.
Also, I think it would be better to check if they have enough money before you even send the "bought" change.
i changed it to this, but it still isn't synced, and it also didn't subtract the cost. that might just be because the money variable is synced though
The set owner and then is owner check immediately afterwards is redundant. You can remove both, and only set owner right before you actually set bought.
I'm also realizing that you are using the player who enters the trigger as the new owner. On Trigger enter fires for every player who enters it. So player 1 will execute the logic for player 2, if they enter the trigger. Usually, you don't want to set ownership for someone else. You want to check for only the local player to execute the code in this case.
actually, in my specific case, i'm making a roblox-like tycoon game. so i want anyone to be able to step on the trigger to send this event
Yes, that's what I mean.
oh, actually i understand now
You don't want player 1 to trigger something for player 2, so you have to make sure it's only player 1 (the local one here) to execute the logic.
yeah so, only the localplayer sets off the change, and everyone else would recieve it because of the synced bool
i previously had the whole GreaterOrLessThan thing connected to OnPlayerTriggerEnter by itself, and it was synced, but sometimes it wouldn't buy for everyone. so i'm just improving it now to make it reliable
Make sure the object is set to manual sync.
And definitely only set owner to the local player right before you do set change, it will make it easier for yourself.
yeah, i've got this now
Yeah, that should work. Btw, is money global in your game? So everyone shares the same resource?
yeah, it's like a co-op tycoon rather than multiple separate ones
Yeah, then your script should be fine imo
how can I get the udon behavior of a gameobject? I just want to iterate through some gameobjects and send a customEvent. For some reason I cant use an array of udon behaviors or it will cause a unity error.
you have to get the component from an array of gameobjects, since the UdonBehaviour[] is bugged
AH. almost had it. Just amde a mistake with the type
just to check, non-owners can send networked custom events on an object that's owned by someone else, right?
@random parrot network events can be sent by anyone, to everyone or only to the owner of the object
Is it possible to toggle the object sync component on/off by script? I dont see a checkbox on unity and using newBall.GetComponent<VRCObjectSync>().enabled = false; doesnt seem to work
I have the 'newball' object parented to a dropper object and the ball seems to lag behind when the dropper moves to other players because of the object sync. I was wondering if I could turn off the object sync while its parented to the dropper still
You can't disable object sync. You will have to use parent contraint to lock the object to its container.
ooh i forgot about those, I will look into it, thank you
if someone is a network owner for an object, are they also owners of that objects children?
if I use the dropper example from before, I have a ball with object sync as a child object to a dropper, who ever owns the dropper will also be the owner of the object synced ball? if I change the owner of the dropper with scripts will it also update the ball owner to that same person?
Ownership is on each individual game object, regardless of object hierarchy.
okay thank you
Hello! I need some assistance with a networked timer.
As of right now, I have successfully implemented a 1 hour countdown timer into my world. It's designed in UdonSharp, the timer itself is displayed as a TextMeshPro component, and the countdown script is called using a networked event in an udon graph. It works like a charm for any players already in the instance, however I need this to work for late joiners too. I have already experimented with OnDeserialization(), and I've managed to get the timer to start from the beginning for anyone who joins late. but obviously the late joiner's timer will be desynced from the people who are already in the instance. I need some way to pull the time from the instance master and carry it over to the late joiner.
How would I do this?
@buoyant ginkgo server time perhaps. owner stores og server time on start as synced var, joiner asks server for current one, performs delta between it and og time and substracts it from its own timer https://udonsharp.docs.vrchat.com/vrchat-api/#methods-4
gotcha, but I forgot to mention. The timer is designed to start using an OnInteract() method. so it won't start right away when the instance is first created.
does it matter? check ondeserialization+og timer not 0
then it would start for both joiners and peeps already there
oh alright then, I'll give that a shot and see how it goes.
How would I achieve this in the udon graph though? would you mind providing a template?
not me, im on phone
fair enough lol
dw, I'll figure it out. Thank you for the advice! š
got it working āØ
is this synced between players already, since the object has an ObjectSync script?
if not, (because i don't think the explosion would sync), how could i make it so it is?
Object Sync synchronizes only physics, or rather the location of the object.
OnDrop does not happen on remote clients, but onplayertriggerenter does, so it would be partially synced but not really work. I would recommend:
- Have a synced variable which indicates whether or not it is active
- Use ondrop to set it to active
- Use OnDeserialization to set the animator to match the synced variable state of whether or not it's active
- OnPlayerTriggerEnter, check if islocal and if it's synced variable is active. If it is, then do the following:
- Respawn the player (Or whatever damage thing you want to do)
- Take ownership
- Delayed event to clean up the object (respawn, set to inactive, return to pool, whatever)
- Send a network event to play explosion effects (So everyone will run the explosion effects, but only the owner will clean it up)
alright, i can try doing that
does this look correct?
hard to tell, can't read everything at that size. But it looks about right. I'm not sure where the cleanup step is, but also I don't even know what you want to do for that in the first place, if at all
anyone have advice for testing networking? if i launch two test clients do i have different display names, or id's or something? do i need a friend?
The different players have different PlayerIDs.
The first player that joins an instance will have ID 1, the next gets 2.. etc.
pog
does the id ever change under any circumstances? like if a player leaves does that id get replaced?
if you rejoin you will be given a new ID. But the ID of players that stay in the instance will never change. The IDs are never reused.
if you create an instance, join it (you now have ID 1) and then rejoin 299 times you will have ID 300
awesome, thanks!
i'm still having trouble getting this to sync
what i want is:
on drop, play the sound and after 6 seconds, turn on the collider, set pickupable off, and set the animator to the state of the active bool
then, when someone enters the collider that was turned on, it respawns that player, turns the collider back off, and plays the second sound and particle effects
and after the second part happens, i want it to wait 3 seconds and set the active state to off, turn the animator bool to false, and set pickupable back on
and i want that to be synced between all players
Why should it work?
For synchronization, the OnDeserialization event is needed for non-owners. Or OnVariableChanged for everyone. Remove RequestSerialization, switch synchronization to continuous. The set owner should be in front of set bool, not just anywhere. In the set bool, enable the SandChange option and most likely it will work right away.
question: what is a functional way to pull PlayableDirector (Timeline) time data from the owner of an instance, and give it to late joiners?
im not sure if I have the right idea here
I think this is how it should work.
gotcha, ill give that a shot
is the double in this a synced variable?
Yes, and this is for manual synchronization.
You can do this through a networking event, almost the same.
gotcha!
i have this now, and everything works, except for the "explosion event" group
the whole explosion event part is completely local to whoever enters the trigger
i didn't get it to work when i tried your version.
but with this, i got the timeline to play at the beginning for a late joiner when they join. buuut, as i said i want it to grab the current time from the owner and bring it over to the late joiner.
im not sure what to do, because your version didn't seem to give the timeline data to the late joiner
hmm
Add a log node and place a double variable there.
Or better yet, two log nodes.
One for transmitting and one for receiving the variable.
This should work too.
the logs are not showing up in the console???
also, it doesn't seem to work either š
Replacing playergined with interactive. Add a box collider to the object. and test in accelerated mode
got it, and also, what is "accelerated mode"?
ok, i might've got it to work, let me try testing
the timeline value shows in the console now
I don't know what you're doing, but there's no reason why both options wouldn't work.
Hi, hope this is the right channel for this - I have an issue with manually syncing variables, and want to check what i'm misunderstanding.
So I have a player, spawn an object from an object pool, when they do this;
- The local player is set to the owner.
- They get a udon script on that object, and set 2 synced vectors, synced 2 ints and 1 synced float.
- It then RequestSerialization,
- Using OnDerialization on clients, I snap the objects positions, rotation, scale, and other data, using the 5 synced variables above to match the owner.
My understanding is RequestSerialization and on OnDeserialization, while maybe a bit delayed, it would make sure to send the data from the owner, to be acted out after they are received on the clients, and can be triggered immediately after the values are changed on the owner. Since I only need to update the objects position once on spawn, I wanted to avoid continuous updates with vrcObjectSync.
However when I try using manual updates, the data doesn't get synced to the clients. I have to do at least 2 delayed 'RequestSerialization' calls after the initial spawn for things to take effect on clients.
Guess my question is, does RequestSerializaion have to be delayed? Am I trying to sync too much data at once, too soon after changing ownership, or am I misunderstanding how the manual updates are meant to work? Sorry for the wall of text, I seem to have a work around for now, but want to know what i'm getting wrongĀ aboutĀ this.
You can do RequestSerialization right after SetOwner. It will correctly set owner first then serialize the data.
Except when 2 or more players trying to run the same SetOwner at the same time, the result is unpredictable.
In my test, I only have one player attempting to claim the ownership of the object. They locally tryToSpawn from the object pool, get the spawned object and when accessing a udon script on said object, change the synced variables and take ownership at the same time before pushing a serialization request.
However I only seem to get those variables pushed to clients, if I add a delayed RequestSerialization a bit of a second after this all takes place.
Can you post the code where you try to spawn the object and set variable?
This is the parts of the code on the spawned object, which is called from the code that takes it out of the objectpool, that handle calls and syncing. From my debugging, ownership get's set fine, even on clients, but the synced vars only seem to update, if called at least .3f of a second, after spawning in.
{
//Claim ownership after being taken out of the Object pool
Networking.SetOwner(Networking.LocalPlayer, gameObject);
//Udon Synced Vars
pos = NPos;
rot = NRot;
power = NPower;
typeID = NTypeID;
ID = NID;
//Used for delayed requestSerialization call in local update
counter = 1;
updateCounter = 1;
RequestSerialization();
//Local call for function called OnDeserialization
SnapPos();
}
public override void OnDeserialization()
{
base.OnDeserialization();
SnapPos();
}
public void SnapPos()
{
transform.position = pos;
transform.eulerAngles = rot;
transform.localScale = Vector3.one * power;
lr.startColor = spellMaster.playerList[ID].playerColor;
lr.endColor = spellMaster.playerList[ID].playerColor2;
if(typeID == 0)
{
rend.material.color = Color.blue;
}else if (typeID == 1)
{
rend.material.color = Color.red;
}else if (typeID == 2)
{
rend.material.color = Color.green;
}
}
As it is, I can get updates to go through to clients, but only if I call them on the owner, a second after spawning in again. The initial request, seems to be ignored, or missed.
Which I can work with, but I'm just wondering what I'm missing, since from my understanding of the docs, this should work without the delayed call is all.
I ran into the exact same problem using VRCObjectPool to spawn objects. I ended up with a delay for RequestSerialization() like you mention, plus a retry loop in case it didn't make it through even with the delay (my testing showed it wasn't consistently reliable even with a small delay).
Yeah, that seems to be what I'm getting, happy I'm not the only one. For version 1 I think my plan will be to stick with the delay call.
I think in the future, if I were to rebuild this system, I would have players take a object from the pool at spawn, and then hide it, to keep in their own storage till use. Then have them top up again, that way the item can be ready well in advance of use, and hopefully no longer have any spawning based delays to var syncs.
Though, I also wonder how much of this, might be due to testing two builds on one machine, over being truly over the net, but oh well, at least I seem to have a work around for now, hope it's something that get's ironed out with vrcobjectpools in the future.
If it's an item per user, take a look at this: https://github.com/CyanLaser/CyanPlayerObjectPool. My experience with it has been great so far.
Sadly not, multiple items per user potentially which is why I wanted to use the pool in the first place but thinking is, if the problem is a delay before syncing works, then giving each player 1 item in advance, not perfect, but could work maybe.
And yep, the cyanplayerobjectpool has been really useful, been using it to store player info, and as a middle value holder when needed.
I'm setting a user as the owner, writing a value, requesting serialisation, then doing a network event to users that uses the value, but users don't ever appear to get that updated value. Is there anything I'm likely doing wrong there? (the script is set to manual sync)
Set the variable as synced? Difficult to know unless you post your script
Why mix network event and synchronization?
How else would I pass a variable along to another function?
Never assume the variable arrives before the network event. Do not mix them.
Use OnDeserialization to do stuff after you have received an update to the variables
Set Program Variable https://creators.vrchat.com/worlds/udon/graph/special-nodes/#set-program-variable
These are "Special" nodes. This includes flow control and special Udon features.
How do I stop it firing for people who've just joined in that case? I could do a "have they been in world for X" but that feels very hacky.
this is clearly not what they are asking for.
DeserializationResult contains sendTime and receiveTime
Ahh, interesting...
if you get the difference between them you can figure out how stale the data is
Yeah, that feels a lot more sane, ta.
np
Just to make sure I'm not being dumb, this would basically return true if the message was originally sent less than 5 seconds ago, right?
Yep
Cool, thanks.
Speaking of which... I'd be interested in a discussion that might standardize this type of thing. Much like the one we had about sorting and the one about delayed start calls I'm looking for a pattern that can be used to "simply and reliably" adapt to cases where the sync'd property should be ignored.
Can you give an example of such a case?
Does Deserialisation immediately run for the person that requested Serialisation, or only other users?
and on a related note, I assume I'm missing something here because the value doesn't update when triggered by the non-owner...
(there's a request serialisation further down the chain)
Deserialization doesn't run on a person who requested serialization.
I assumed not, thanks.
You can still call OnDeserialization on the owner itself
I have several and there are definitely other postings here by others that relate to what I will term "actions" rather than "states". I want my NPC to "jump" so I set the synced value to indicate that. It jumps for all the players in the room. A new player joins and since that is the last "action" received the player jumps for the new player. Nobody else sees that particular jump. I would rather not concentrate on the particular example however. There is no way to set a Boolean to true, have the current players act on it and then to sync that value to false so players don't perform the action.
Deserialization happens when receiving data. It doesn't happen for the sender.
Would an event provide what you are needing in that case (for a bool in particular)? You'd still need an age check. For other cases, I think the solution is an event with a parameter, which seems to be possible with the 3rd party solution from Miner.
Yeah, that's fair.
I should re-review the use of events in my systems. I've gotten into the pattern for syncing properties and resort to that most often. I also have a little pub/sub system that works quite well but it doesn't sync between players. Maybe an event can serve as a transport mechanism in such a case.
I've reviewed the code by Miner. Quite the undertaking but I haven't bit-the-bullet yet to implement it. I'll try to abstract the age check into something that can easily be set and monitored.
Meanwhile scriptable object support would be marvelous š https://www.youtube.com/watch?v=raQ3iHhE_Kk
Scriptable Objects are an immensely powerful yet often underutilized feature of Unity. Learn how to get the most out of this versatile data structure and build more extensible systems and data patterns. In this talk, Schell Games shares specific examples of how they have used the Scriptable Object for everything from a hierarchical state machine...
When using an object pool, do you have to set owner to the pools game object before doing requests? Trying to understand how it works with U#
@warped latch yes only the owner spawns and returns the objects
so either the object itself calls a return network event to the owner or you transfer the ownership of the pool first
I hope that makes an easy discord search for future people
its in the docs
I swapped out my udonsync stuff with my actions class and made them CustomNetworkEvents. I like this much better, the boolean problem is easily handled with ActionOn and ActionOff events. That leaves us with how best to handle parameters when they are needed. I am loathe to tie in sync'd properties and some arbitrary waiting period even if it worked in the majority of cases. Have you tried the solution from Miner?
Parameter values could be "pushed" to a service but that's a fair amount of overhead in both directions.
No, I haven't tried Miner's solution yet. I need to look through the code sometime to get an idea how it works.
Also, I should add that for bool you probably just need "Action". When you receive it, you perform the action. No need to maintain a bool.
Yup... but on and off events can denote which way the switch should be thrown. Could be a toggle I suppose but it could get out of sync as people join. It could easily handle multiple settings if there were on a few well-known ones (like 3 or 4 colors or sizes or something) but beyond that it would get messy.
Ah, you want to do something different as the value changes. That makes sense. My cases are more like "make a noise when something occurs".
I'm trying to get as much mileage as I can out of things that don't need to be sync'd š
It can already be "tricky" formulating patterns for sync'd state but incorporating custom network events adds a wrinkle. Eventually I'm going to have to update my standard Udon class structure to handle additional combinations. (That probably sounds odd) but what I mean by that is there are states that need to be initialized, some of those need to be sync'd with new players. Action type events can include a sync'd UI component (a check box for instance) that lets me know I have called the event for players already in the world. New players won't receive the "on" action (and we don't want them to or it would have been state-based) but they will receive the "off" action when I change it again. None of this is particularly hard to write but does involve a lot of thought to coordinate.
I may have to investigate Miner's library sooner rather than later. He is basically mimicking what the Photon library does under the hood with RPC's which is fundamentally how Photon (and VRC) sync'ing works. I think It is easier to transmit state directly than to set it and then to coordinate it among the players.
I got syncing issues
I basically have the following code, butfoois not getting synced across clients
[SerializeField] private TextMeshProUGUI tmp;
[UdonSynced] private float foo;
public void Increase() {
foo += 0.5f;
RequestSerialization();
}
public void Decrease() {
foo -= 0.5f;
RequestSerialization();
}
private void Update() {
tmp.text = foo.ToString();
}
(My implementation is very different, but this simpler version produces the same issue)
Every player can locally change the value using UI buttons that call either methods, and the change shows up correctly in the TMP Text as well. But other players do not see any changes made by other players and only have their own unique, isolated value.
The script on the game object is set to manual syncing mode.
What am i doing wrong?
only the owner can send synced variables, so you need to add Networking.SetOwner(Networking.LocalPlayer, gameObject); to tbe Increase and Decrease functions. Also, using update to print the text is very inefficient, use OnDeserialization instead. This is an event that happens when you receive synced data. Though it does not happen for the person who sends data, so you should print the text on Increase/Decrease as well
So it wouldnt be possible to have a single button which everyone can use to change a shared value?
But during the testing, one person mustve been the owner. Why didnt their change get synced to the others?
Can someone help me understand on how to make a certain toggle in the game network synced? Like when someone joins the world that same world toggle is enabled for them too
@undone ridge interact is called only for person who clicks the button
so they must be an owner in order to update smth for everyone (or call a network event to the owner but its not the best idea)
@static crest theres already u# network toggle if you want to keep it simple
just add is as component on interactable object
and populate the array of objects to toggle
synced toggle? dont remember the name
So as of currently, the toggle is turned off, do I add the network toggle to the button or the toggle itself I'm very new to world building. I have a pool table prefab and would want it to be network sycned for all new joined players
@static crest to the object that has interact, so button.
So I've got cyantrigger setup for that button, do I add a new component
no idea what and how cyan does.
Where it says 'anyone' 'send to all' that sounds like whatever state the button is at, it should show for other players?
Been playing around with it more, and even used SendCustomNetworkEvent to try and change a local value but its still all not synced
Its as if any network calls aren't even reaching anyone else
Do they even work in Build & Test mode?
Are you testing with the code you provided as the sample?
Tested some more and the sample by itself works fine. But i am using Instantiate() to create new objects (which hold that UI) and it seems those are not synced and only created locally. And Network commands dont affect them, or something.
If i have one of those objects in the world from the start, it works fine and the value is synced between clients.
Is there any way to Instantiate a "synced" object?
Instantiated object can't be networked. Use object pool instead.
Thanks
I'm really new to udon and I'm trying to sync an animation across players but I'm not really sure how to go about doing so. Currently I toggle the blade based on a boolean that plays the animation when the player uses the pickup.
// Current blade state
public bool isExtended;
public override void OnPickupUseDown()
{
toggleBlade();
}
hi someone help me , im try U# about teleport with other player same time , how it work ?
You want to teleport all the players at the same time?
yes
public void _TeleportEveryone() => SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(TeleportLocal));
public void TeleportLocal() => Networking.LocalPlayer.TeleportTo(transform.position, transform.rotation);
okay i will later thank you
Extremly new to coding and Udon, I have this prefab, thats supposed to be used in actual games, Blaze AI, however when ran in VRChat it doesnt seem to work. I fear its a problem with the actual scripts, and now im scared im gonna have to start editing it myself
These "Jolly Rancher Elites" as my server calls them, are supposed to attack the red box, but in VRC, doent even move.
All scripts in vrchat must be UdonBehaviours. You cannot upload arbitrary code from external assets made outside of vrchat
at minimum, that means changing all MonoBehaviours to UdonSharpBehaviours, and adding the UdonSharpProgramAsset. But most likely, they will be using some additional features of C# which are not supported in Udon, such as native lists, inheritance, custom structs, non-behaviour custom classes, or scriptable objects
So how can I do that?
Just change this?
and these?
If that's using unity events and system.collections.generic, let alone whatever's happening in BlazeAISpace, then it will require someone knowledgable with udonsharp to go in and do a bunch of manual work to replace it with equivalents.
So what do I do
I dont know how to code im kinda stressin here
You could watch this udon-specific tutorial to build something from scratch https://www.youtube.com/watch?v=xVXXS7HX7Og
In this TLX Spring 2021 session, Centauri focuses on how to create simple local and synced AI, and different ways to sync it between multiple players.
Check out other talks from the TLX Spring 2021 event at https://www.youtube.com/playlist?list=PLTgqlzYxsEMxiUvVaqBcD5OL9MpdmkiSH
Learn more about Prefabs TLX at https://tlx.dev
Follow TLX on Tw...
or you could find a vrchat-specific AI prefab such as this https://github.com/Centauri2442/SimpleAI
I tried that.
The issue there is its TOO simple
I need to be able to combat these AI, which is why I use Blaze, for the cover shooter functions
and the general open endedness and it just feels easy to use
not like blaze would be networked anyway
I also dont have to watch an hour long video just to figure out how it works
I don't know what to tell you, what you're asking for is complex and requires a lot of know-how. You're gonna have to either learn it yourself or hire someone
I assumed Udonsharp did it for me, isnt it supposed to convert C# to something VRC can use?
UdonSharp is a subset of C#. It does not support everything
A compiler for compiling C to Udon assembly
VRChat is currently working on Udon 2, which will have far better performance and support the entire C# standard. You're welcome to wait for that. But again, it's still very unlikely that this AI package will work out of the box, as things like network syncing are still VRC-specific
Hi, can someone help me to modify my sync toggle so it will work for animations of blenshapes as well? Like I want to make an elevator that is synced.
hi that work but i not mean all player , like game world Murder 4 , who Join in zone and teleport this, not zone other out player
You just need to check if the local player is in the zone then
In the TeleportLocal event you branch on if the local player is inside a zone. You could for example use a trigger collider or check the player's distance to a position.
i did add use public VRCPlayerApi[] playersInTrigger = new VRCPlayerApi[20]; and OnPlayerTriggerEnter
you don't need to keep track of all the players
just if the local player is in the zone
you dont mind you can little how code ?
U# or graph?
using UnityEngine;
using VRC.SDKBase;
using UdonSharp;
using VRC.Udon.Common.Interfaces;
[DisallowMultipleComponent]
[AddComponentMenu("KitKat/ZoneTP")]
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class ZoneTP : UdonSharpBehaviour
{
[SerializeField] private Transform targetPos;
public bool _inZone;
#region API
public void _TeleportEveryone() => SendCustomNetworkEvent(NetworkEventTarget.All, nameof(TeleportLocal));
public void TeleportLocal() { if (_inZone) Networking.LocalPlayer.TeleportTo(targetPos.position, targetPos.rotation); }
#endregion // API
#region TRIGGER OVERRIDES
public override void OnPlayerTriggerEnter(VRCPlayerApi player)
{
if (!Utilities.IsValid(player)) return;
if (!player.isLocal) return;
_inZone = true;
}
public override void OnPlayerTriggerExit(VRCPlayerApi player)
{
if (!Utilities.IsValid(player)) return;
if (!player.isLocal) return;
_inZone = false;
}
#endregion // TRIGGER OVERRIDES
}
ah , okay i need read for learn how that work , thank you so much ā¤ļø
i see i see i see i got it !
Nice code layout š I've always made heavy use of regions and the grouping of properties, methods, etc. I was totally unaware of those attributes though I use HideInInspector and a couple of others. I see you have a Utilities class BTW. I have a similar one. Helps eliminate a lot of boilerplate code while insuring every occurence uses the same syntax.
Thanks :)
The Utilities class is part of the sdk btw
Ah yes of course. I wrap them in my own class so I can construct single methods from combinations. So for instance
public static Boolean LocalPlayerIsInstanceOwner()
{
return LocalPlayerIsValid() && Networking.LocalPlayer.isInstanceOwner;
}
That unfortunately has a fair bit of overhead
no where near the overhead as someone with a large avatar or 50 people in a world
part of the build process could be to in-line all the static calls and remove the class should it become an issue.
Quick question with UdonSharp.
Has the issue regarding synced variables not being set immediately after Networking.SetOwner() been resolved?
I'm a bit confused by the wording. If you're referring to the inablity to set synced variables within some time after taking ownership, then the answer is that yes that was resolved a couple years ago. You can now safely setowner, set variables, requestserialization all at once
Perfect. Thanks. I noticed that issue was still on the documentation.
Under networking tips & tricks.
[[ quick networking help ]]
i dont normally use UI for buttons, ive always just done mesh collider trigger but wanted to try UI this name. but it seems all my regular global scripts ive used forever arent global when on UI? this is how i have one set up, it is supposed to play an animation globally, which works when gameobject is the trigger, but isnt showing the animation global, just local on UI. would like some guidance pls!
SendCustomEvent only sends an event locally. You'd need SendCustomNetworkEvent if it's available or create a method that calls SendCustomNetworkEvent
yeah the secondcustomnetworkevent isnt available in that top drop down, thatās why iām unsure. itās added in the scripts but wasnāt sure what other component needed added to UI for it to work global
@lost tapir Do your button have the VRC Ui Shape script too? You can't send Custom Events without that
yep yep! like i said the button does work, but the animation plays locally, not globally despite having a SendCustomNetworkEvent within the script.
I believe I found a work around by dragging the button to the slot under runtime and changing it to Event Interact. the people helping me test seem to see it globally with this change. hoping this was a good enough work around, ty everyone !!
You only need the VRC Ui Shape on the canvas.
It's good to know, I have put it on all on my child objects, except the canvas, ty
This sounds very strange, can you show the graph?
Ok, so I have a door controll system that allows for locking the doors, I have the 'Locked' state set to UdonSynced, but it seems to not function for late joiners.
Script is set to 'Continuous' synchronization method.
Been a while since ive done networking for Udon, and testing for this quirk is somewhat difficult
Additionally in the Start method I check the synced variable and handle it as needed.
@vale prawn Is your script contains the OnDeserialization() or value Change functions where you handle the late joiners?
I dont use OnDeserialization at all, is that now required for this?
Never used to be like a year ago when I last worked on this script
You don't have to, you can use custom network events too
I use 2 custom network events, Lock and Unlock. I use a single ToggleLock function on the person locking or unlocking, which determines which event to send
That all works perfectly fine. It is just anyone that joins while the lock is in the non default state, in this case it is initially unlocked, so anyone joining once the door is locked sees it as unlocked
when a new player joins, he should request the actual door state from the owner of the object, that can be done with Custom Network Events, but using OnDeserialization() can be good for dealing late joiners, because it gets called every time if someone joins to load all the synced variables
This is making less sense, I thought they received the synced variable upon joining, and would then have it when the Start() method is triggered?
if thats how things work now whats even the point of [UdonSynced]???
They do recieve all the synced variables, but not the logic of your game. you still have to call the OnDeserialization() and set the value that is stored in your udonsynced variable
Hol up, so they receive it, but its not automatically set into the variable like it used to be??
yes, you still need to call the gameObject.SetActive() method when they join, even if you use udonsynced variables
I do
I use the start method to handle that, should I instead be using the OnDeserialization method for that?
yeah, i think when the game launches, it won't initially have all the synced variables, it recieves a bit later after the Start() method
Wait, do I not have to use seperate custom network events for toggling things anymore???
Or is it still best to do that for people currently in the instance and use OnDeserialization to handle it otherwise
I havent touched udon since all the networking update
if you are using UdonSynced variables, you don't need to send custom network events to update the variables, players receive the variable updates when the OnDeserelization() method happens
How stable/fast is that though?
I know back when udon was sort of new it was impossible to rely on that
I didn't do experiments on it, but you can also change between manual or continious mode. manual means, you have to call RequestSerialization() on your script, otherwise your synced variables won't update on that script
Reading about manual means only the owner of the object can do that though, and since this script is on an empty object I have no clue how it would change hands to whoever triggers it
It shouldnt hurt to just keep running custom events to trigger specifics
I don't wish to be a stick in the mud but from what I'm reading there are lots of questions about how network sync works and you would benefit from watching/reading a tutorial and experimenting some to get the patterns down.
you also have a Global Toggle Object script by default in your scene probably, that you can use to toggle your doors globally
I didnt know the whole of networking completely switched up from how it used to be, never even knew OnDeserialization existed or how its triggered, documentation seems to be lacking on how/when it is actually triggered.
Pardon me but that code is suspect for a number of reasons.
What's wrong with it? it works perfectly
As I've told developers for decades, working is what makes it code. It isn't working efficiently. If you want to post the code I can show you sample improvements.
If you'd like a few simple examples.Start() is setting an UdonSynced property which (if they are a late joiner) not going to keep that value. Only the master needs to set that when the world starts.
No benefit (in my mind) to check if the gameObject is already owned by the player who Interacted just set the player. It causes no issue if they were the owner. "Simple is better".
You have toggleObject.SetActive(isEnabled) twice. OnDeserialization won't be called for the non-owner and the owner should (probably) set something active "after" it has called RequestSerialization.
I can see certain cases (Based on past experience with how Udon used to work at least) where calling SetActive would be reasonable on the interact and when the variable is synced
I personally would add two methods. One is called by Interact to do whatever that class needs done. It keeps Interact pretty empty and permits other code to call that method without calling Interact.
The other is a method I add to any sync'd class which I now name Apply. OnDeserialization will simply call it and the player that sets the value and calls RequestSerialization will call it. Everybody calls the same method resulting in the same behavior.
but this script also transfers the ownership of the object, that's why it's checking if you own it or not. when you transfer a door, on the first door open, it will open up slowly, because you didn't own it, but if you keep opening/closing the door, it will be faster for you, until someone else start interacting with it too
private void SetPressed(Boolean pressed)
{
AppApi.LocalPlayerSetOwner(gameObject);
_isPressed = pressed;
RequestSerialization();
Apply();
}
You won't have my AppApi library but it clearly sets the gameObject owner.
It can't make a difference in this case. If you are not the owner make them the owner if they are the owner no change but in either case set enabled and keep going. Nothing changes if you just set the owner.
if( x != true) x = true;
there is no reason to check that
Ok so one last question, is there a way to test udon networking Without loading VRChat (I only ask cause Im on a network that I should not be uploading or logging into VRChat from)
Clientsim gets close it seems, but not close enough
If there isnt (And I dont expect there to be) then Ill just have to wait a couple hours to run some tests
not really
Your suspicions are correct! š
you can use local test which avoids the upload, but you're still talking to vrc servers since the clients are open. The sdk may also talk to vrc servers already even if you're not logged in fwiw
Yeah, Im not worried about the sdk talking, its more so a large amount of data hitting them that I want to avoid lmao
Oh definitely test before uploading the world. Much faster turnaround for what will almost certainly be unforeseen errors.
yeah, i just had a thought it would notify all the players if you don't check the ownership, so if you would have 30 players online, they would recieve an ownership transfer notification even if no owner was changed, but actually, in game it blocks you sending ownership transfer to yourself once you already the owner. so yeah, the check is actually not necessary
Yeah, Ill test later tonight when im not on my work vpn
Oh wow, network is much simpler now than it used to be
Just stripped an old script that was ~100 lines down to closer to 50
HOOOOOLD ON Let me run this idea by here quick,
So, since VRC has 'Allow Untrusted URLs' lets say I join a world with a script that pulls from an untrusted URL, and sets the value to a synced variable, then someone else in the world joins and does not have untrusted url's enabled, if I understand correctly, the same data that is pulled by one user is then able to be shared to all others who would not normally be able to access it?
If this actually works as I expect the implications and very strange abilities of it are immense
The dangerous part isn't what the URL retreives. The dangerous part is the information that is sent OUT along with the webrequest.
Agreed, and im not saying its a bad thing this is possible, I am actually heavily interested in it and the realization that I could do something weird like host a local server only accessible by me, which would allow me to change data and be the only possible person to change that data externally. I can think of at least a few things that would be extremely interesting with that
If it is "dangerous" I would like to read an example of what that danger is and it would be nice to see how the current implementation prevents such a danger. Lots of people are hosting REST services accessible via VRC.
Does it expose your IP address, when you recieve data from an untrusted url?
only if it's your client who made the request
in the scenario where client A requests the data via http and uses udon networking to send to client B, client A's ip would be exposed, client B's would not
danger is relative :D loading patron/VIP/whatever lists over http is susceptible to spoofing for example
It doesn't know where you live. There is a rough idea about the "ISP provider's network hub". Your IP address is known by everyone with a web page and each posting you make here.
That isn't limited to VRC. Communication over the Internet should be encrypted and HTTPS is the only method for data to pass out of or into VRC as far as I know. That someone can print counterfeit money doesn't suddenly mean that nobody accepts cash.
But again it is simple question... how is what VRC provides now keeping people safe?
That doesn't have anything to do with REST services does it?
I'll point out that VRC currently has a "player option" to enable untrusted URLs. This is an on or off switch "for every world in VR Chat". If security was important it could be on or off on a per world basis. Someone might trust you but not me or vice versa.
IMO it barely is, there's no user feedback on http requests, the untrusted url control is way too coarse (trusted domains [which gets updated at any time without warning by vrc] or Literally Everything), there are so many worlds that require untrusted urls to function at all that people just leave it enabled forever, and despite all the restrictions there have been quite a few "creative" methods of exfiltrating user data
I find this an interesting conversation. So what "user data" might people with VR Chat world know about me?
and yeah that's half of my point, it has bothered me for a while. a little bit more user feedback would be nice
display name is the obvious one, instance size, position in the world, interactions with world elements, by correlating requests from other users you could identify who else is in the instance, etc
Where in the world you spend most of your time, who you spend most of your time with, how often you go to their world, etc. etc.
My display name is not a secret is it? Position as in "where I'm standing"? So the "Coffee Shop" knows that I clicked on a cup and when I dropped it, it was floating in the world and someone else picked it up and dropped it several times. The Coffee Shop (example) has no idea who I spend time with only who was in that world. But let's use a video playing world (anybody got one of those?). Now that it knows I used to drop in every night with 3 or 4 of the same people (and sometimes a few other people) but I haven't in several weeks... they take that information and do what? Redesign their world maybe? Doesn't sound like user statistics would be such a bad thing.
@cold laurel So you have untrusted URLs turned off? So as to not be tracked?
nah, I have it on
i don't think it's inherently a bad thing, and it would be nice to see some of it added to vrchat natively at some point. even the simple stuff like the pc vs quest user percentage would be good to know. average instance lifetime. etc. and vrchat doing it at the platform level would help alleviate some of the privacy concerns - it can be guaranteed anonymized that way
From my limited understanding it's* restricted due to user data protection laws 
(it's* here referring to sending data out of an instance)
Again I'd be happy to hear where you heard that from. It would apply to VRC and not to your browser? Type "what is my ip address" in your browser.
more so the restriction on constructing VRCUrls at runtime
because that would make it ridiculously easy to send data out
Yes and I think that restriction should be removed in most cases. Not hard to institute a "developer" subscription with a legally binding agreement. If a world builder is found to have broken the terms they can be fined, their deposit will be lost and they lose access to VRChat.
i'm sure something like that is in place for the allowlisted services like vrcdn. not sure how well it would work for individual creators though
I don't "know" of course but if they can ask for $10 so I can store more avatars they might ask for $25 more and give me direct access to my REST services.
Other systems have found ways to do it. AltSpaceVR prompted the user once per world when the call was made as did NEOS (and now Resonite).
i think that's a good middle ground
it is also proactive rather than reactive
need some help here
If String Equals is true, to is valid object then "SendCustomNetworkEvent" would run
else it wont run right?
But the shit is, even i have it false. it still runs.
whats going on.
O
Is this correct?
@sweet wolf @humble girder
Does this set Key 1 and Key1 hidden box location to DefaultLocation 1?
Did you test it?
yes the position went like wrong
Of course that isn't correct.
so.. whats wrong..
TranformVector. You have to use Get Position instead
i am grabbing the location of default location
like this right?
damn when i use that node.. i feel something is off.
How would they export your display name given that URLs cannot be constructed on the fly and cannot POST data?
Definitely not law related, VRC are just being super strict for the sake of caution. It's a shame because dynamic URL generation would make a lot of things exponentially more efficient and allow for a lot of keeping things dynamically updated rather than having to push world updates.
Charging world creators for access to the creators own services is just a kick to the face. You're charing the people who make the content for your platform which is why anyone is there in the first place for them to be able to access their own services, which they're also likely paying for out of their own pocket. That's an insane idea. Having some sort of unlock or agreement would be fine.
(unless I'm misunderstanding what you meant)
Resonite allows for dynamic URL generation and it lets me keep things in world updated without having to touch the world, allows for per-user customisation, etc. It feels like VRChat is HTML before the advent of dynamic page generation became a thing, but only due to it's own self-imposed restrictions.
one or two characters at a time with premade urls. can also hash it first to get the length under control. there are multiple worlds doing this already
How do i play an event for a set duration?
can you elaborate?
this logic is flawed.
its gonna loop 25 times all at once
to this Event.
š
LOL
i wanted it to play this Event every 0.2 second for 25 times.
yeah i know whats wrong.. lmfao and i am trying to figure out.
let's move to #udon-general
ok?
Content creators often don't seem to realize that without the server there is no host for their chill-out world. A store pays the mall owner because the mall itself brings in customers. The mall does not pay the store but in an extreme case might charge less per square foot because the store is an anchor and will attract a) other stores and b) customers. I can pay someone to create a VCR world ... how much would you be willing to pay someone to build and maintain VRChat?
Additionally Resonite (and NEOS) worlds are hosted by the person who instantiates the world. Resonite doesn't (in so far as I know) direct all the traffic.
That's why they take a share of the creator economy and have VRC+. But charging the people who are making their content is like having a job where you pay to work there.
You're saying I should spend all my time making worlds, paying for assets, then pay for servers to run backend stuff but also I should then pay VRC to be able to access my own backend? That's absolutely ridiculous.
Keep in mind I pay for and maintain those servers which again is more time and money on my side.
I like it... a set of URLs that match the character set. Maybe some special ones for common strings š Sounds like a lot of work to get my user name.
Users host the session (and handles the intra-user traffic e.g. movement and voice) but Resonite hosts the assets.
First no it isn't. Nobody is "charging" any more than they are charging you to save avatars. You get more avatars for plus that's all.
You shouldn't spend any time doing anything you don't want to do. I should be able to buy more bandwidth, server space and stuff if I want to.
Your suggestion was charging people (who made the world) to access their own external APIs (that they run and maintain themelves).
It sounds like you're suggesting paygating peoples own content from themselves.
Okay fine you don't understand. If Microsoft had hosted the MRE services they supported it would have been easier for a contributor to set up. That contributor had to pay someone so why not some extension of GitHub or something? Stuff cost money I'm sorry to say that but it is true.
Well there you go.
That isn't how VRC works which is what I was saying.
I don't think I'm the one not understanding here. We're talking about world creators ability to have their world's connect out to the USER'S own services. Not services run by VRC. This would also be benefical to VRC as it would allow content to be updated in world without the owner having to upload a new copy and users having to all download new copies, thus cutting down on bandwidth usage.
We're talking past each other. I'm quite aware of what "could be done" I'm explaining how business works. If I want open access I am willing to pay for it. If you want to operate a taxi cab service you make a large deposit so if you stiff your customers there is a recourse for the customer. People rightfully so can't trust any old world builder. We don't know who they are.
Sounds like you're working against yourself and all other world creators by encouraging features that require us to do all the work to be pay gated, I'm not sure why you'd do this but sure.
Sounds like you think you deserve to have your worlds hosted for free. You see how silly that sounds? You're angry that's fun but if you want to host a world with 50 people in it and fire off a REST call per minute you can do it on Resonite (on your dime) or write your own app. VRC isn't (likely) going to permit it, I'm not making the rules.
You understand that worlds making REST calls to user's own servers will not increase load on VRCs servers, right? This functionality would allow for better worlds without any increased cost on VRCs side.
But anyway, I'm done. I'm not going to humour these outright fucking stupid suggestions any further.
@humble girder remember that stencil thingy? Is it possible to like set a range that what can see through?
I said we realize you are angry...
Not by itself. Should need to use trigger collider to switch material when in range.
wat do u mean?
like lets say i see through the magnifying glass
i want to see 10 meter of hidden stuff etc.
how do i make that?
There is no range for stencil. The only way is to switch the material out so it doesn't show up.
Oh. ok
That makes sense (it is only necessary to explain stuff you know). There may be some residual impact however and there is always an issue of responsibility. Some world builder does something illegal and VRC will be blamed because they have the deep pockets. Again your argument is not with me, write VRC a letter or start your own competitive service.
@humble girder @sweet wolf Hmm how do i fix this issue.
okay i try to explain.
When a player in a list joins a world, a tag gets lit up.
When the second in the list joins the world, their tag gets lit up, however they also have to tell player 1 to lit up their tag too.
How do i do that?
Are you trying to enable a specific GameObject if both the GameObject's name and player's displayname matches, then sync them for all the players?
yes. and when new player in the list joins, it the tag will lit up
now the question i am is how.
you can just call the OnPlayerJoined and OnPlayerLeft events, iterate through all the GameObject's and check if the player's name matches, then enable/disable according to it
yep and no.
See. i explain to you..
when a player in the list joins. their tag is lit.
player 2 joins, their tag is lit too but how do i player 2 tells player 1 to lit their tag in their screen?
same goes if player 3 / 4 ...........
how do i tell a player to tell another player to check if if that partiular player is on or not.
I'm not sure, what are you trying to do, do you have a staff list in your server, so you would like to enable more stuff for them?
The player joins and checks "a list" so does that list contain user names or something else? A second player joins it checks to see two things. If it should light itself (by checking the list) and whether any other player that is already present is on the list. Each player is responsible for itself and "knows" that if other players are on the list and in the room they will have set themselves. Will that work?
This isn't too reliable, is it? Even if the "playerTurn" is a UdonSynced variable, it doesn't update for other players
On the other player's OnDeserialization method it doesn't update
This is not recommended because variable sync and network events go through different paths and may have different latencies. It's entirely possible for the event to arrive first, and use outdated data.
You should instead be using OnDeserialization, which is an event that happens when you receive synced data.
Here's a video to elaborate on this point further: #udon-networking message
it seems the other players runs the OnOwnershipTransfered first, then later the serialization, i had a thought it could be a problem
changing the owner on the OnDeserialization? sadly it just adds more latency to the game, but if it's that's the only case, i will try it.
When you join an instance you get assigned a playerId. The ID you get is the previous player's Id + 1.
The first player to join an instance gets ID 1
If that same player rejoins the instance they will now have ID 2.
playerIds are never recycled within an instance.
So, trying to get the player with the ID equal to playerTurn + 1 is not very productive.
Setting that player as the owner of the object for sync is also not productive because the local player needs to be the owner of the object for the synced data to be sent.
have you played the among us game? mine is going to be somewhat similar, where people need to enter in a zone first, then those players are teleported to the game
You could cache the player IDs of the players that are currently in the game and store it in a synced int[]
i already made a system for that, I'm making a card game btw, I just want the turn owners to interact with the things more seamlessly that time, like drawing new cards, placing cards down etc.
probably not too efficient, but it gets the job done xD
but it's manual sync, and they only get called when player draws a card, places it down
so it doesn't have that much impact on network traffic
oh I just realized I misread this - I thought the third line was sendcustomnetworkevent, and my response was tailored to that situation.
It's actually SetOwner, and in that case my response is slightly different but still it's not going to work because if you set data and try to send it, then immediately send ownership to someone else, the ownership transfer will stop any data you were trying to send.
if that setowner was always to the localplayer, then it would work
yeah, but it's also strange the other player receives the OnDeserialization too, becuase it's gets called too, but it doesn't actually change the variable
so it just prints out the last known value
probably received serialization from the new owner
but the new owner is himself
and he couldn't call that method when he's the owner
I think when you call RequestSerialization() and SetOwner(player, gameObject) at the same time to the new owner both at once, the new owner will still run the OnDeserialization(), but it doesn't update the udonSynced variables, but the OnDeserialization shouldn't even run in the first place
if the ownership already got transfered
i fixed the issue.
How do i make an object that i can hold, place and step on..
It seems that my collider keep getting bugged up.
When i touch the cube.. i keep get flung, or just sink
Let me explain..
I spawn pillows.
I pick up the pillows, i can place anywhere in the world.
I step on it
I get flung like 20 meters.
Why is that so and how do i fix it?
Anyone have any idea how to fix such issue?
I think you can't really have a collider on it while you are also holding it, because it would cause physics problems, but you can disable the collider while holding, and enable it again when you place it down
You need to make sure the pillow is set to the pickup layer. The pickup layer does not collide with players. This should've happened automatically when you added the pickup component, but if you do things in a certain order that doesn't happen
they said they wanted to be able to step on it though
yeah i want use that to build like blocks..
so how?
well, i donāt know why you get flung away when stepping on it
theyāre set to isKinematic right?
if not, try that, if they are, try without it
just to see what might work
yes but.. okay i will try hold up .
why i set isKinematic because i want it to stay in place in the mid air.
And i dont want people to move and drop and get the pillow wander off the world..
hmmmmmmmm
I want to make a Lockable door.
I was thinking about it:
If I look the door, I set the Owner and a locked bool.
Then I woould just block any non owner interactions.
But what happens If the owner leaves the world?
Is the owner then set to null?
i believe that in that case, the owner moves to the next player who has been in the instance the longest
and i think youād need an OnOwnershipTransferred event to tell it what to do
I will look into that, thank you :3
Is there any way to get currently/last spawned item from object pool for late joiner? Because for late joiner when the script starts I need to determine which object from the pool is currently active but no matter if I run my way of checking on Start() or OnPlayerJoined() it never finds the active one because ObjectPool haven't made that object active for him yet
here's how I check for the active object, by default all of the objects I have in the pool are disabled in the scene (I'm also using this when Owner spawns object from the pool and it works without any problem for others)
public void SyncNonOwner()
{
for (int i = 0; i < this.transform.childCount; i++)
{
if (this.transform.GetChild(i).gameObject.activeSelf)
{
carObject = this.transform.GetChild(i).gameObject;
SetCollisionArea();
Debug.Log(carObject.name);
return;
}
}
carObject = null;
}
I could technically attach script to objects in pool and just set value of currently spawned object with OnEnable(), but I hope there's some other way which I don't know
This may sound stupid but in udon graph, how do i tell GameObject A collided with GameObject B ? Object specfict..
In this 2 script, i have this issue where when i join the game, the second player who joins triggered all the chest and stuffs.
how do i prevent that?
something is defo causing when player joins something.
i need to understand Ondeserialization / Request Deserialization..
Where do i place them
When do i use them
how do i use them
The differences.
RequestSerialization to send data, only object owner can do this
OnDeserialization is called for other clients when data was received
sadly i cant help you with udon graph, but for U#, youd simply call RequestSerialization(); to send UdonSynced variables, which may require at least one changed variable
once data is received by others, it will automatically call OnDeserialization(), which you can use to process data if you need to
Debug.Log(someFunnyVariable);
}```
can u explain what u mean object owner? whats the best use?
like as u can see i set the object owner to themself which i think its wrong..
if u can explain how i can grab the "Master" who has the most time spend in the current instance and just copy what instances that player have.. that would be much easier.
you can prevent ownership transfer when returning OnOwnershipRequest to false, you can get master by using Networking.IsOwner(gameObject) for an object, Networking.IsMaster or when using a VRCPlayerApi player.IsMaster, not sure what you mean by copying instances a player has
copying instances the player aka the master have means get all the sync stuff from them.
vrc is pseudo p2p, having all stuff on master is just doubling the delay, sinve you first send the data to master, then it sends it to everyone, instead of becoming the master.
not to mention how everyone loves being a master of 100 pickups and having all this rigidbodies to be simulated on their pc
public override bool OnOwnershipRequest(VRCPlayerApi requester, VRCPlayerApi newOwner) {
return false;
}
will disable ownership transfers, and only master will be able to serialize
is that what you mean?
ahh.. i not good with Udon sharp..
Do you need ownership of a vrc object pool in order to spawn things from it?
Not necessarily. The child objects should already be part of the scene and you can just request ownership of the child object you want from the client's end
Okay let me explain my issue. Lets say u got yourself a lock and a key.
While holding the key everyone should be able to see it.
When the key is collided with the lock the key and the lock will disappear adding 1 to a variable.
When do i request Event OnDeserialization? or UdonBehaviour RequestSerialization?
Who should i set owner to? or dont need?
One key thing you might not know is: each player run copy of vrchat client on their own machine independenly. Therefore each player script will not interfere with each other.
OnDeserialization is an event that react on new data arrived when a player isn't an owner of object and the data is updated by object owner.
RequestSerialization has to be called when object owner want to update and propagate value to other player.
SetOwner to a player who want to update and do RequestSerialization
Do anyone have any idea why does a player refuses the ownership transfer, because it thinks it already owns it, but when I actually pick that object up, then it finally transfers the ownership?
Here, it doesn't transfer Y7-077 back to me, because it thinks I already own it, but when I pick it up, it transfer it to me
Also what is your script?
this is the only place where i transfer all the ownerships
it works fine for player 2, but for player 1, it doesn't
