#udon-networking

1 messages · Page 28 of 1

safe path
#

Without this place, I’d be pretty screwed though

#

Thank you, kind people

twin portal
#

seize the means of synchronization

safe path
#

For the mirror gazers

#

They are rebelling against the “elite”

safe path
twin portal
#

like, in a single frame? or within a timeframe?

safe path
#

Yeah, within like a second or so

cold laurel
#

You would normally want to stay under 5 per second.

#

But it really depends©®™

#

Serialization happens per GameObject btw.

#

This is why you can't mix sync modes between scripts attached to the same object.

safe path
#

I did not know the other stuff though so that’s very nice

#

Will save me many future problems

cold laurel
#

Also, you should use Manual sync for 99.97% of things.

#

When you say that someone is "on quest" do you mean them being on quest standalone? Or using a quest tethered to a PC?
Because a quest tethered to a PC is the exact same as an index tethered to a PC.

north thistle
# cold laurel Also, you should use Manual sync for 99.97% of things.

Borrowing from how Bungie detailed their network design in their GDC talk on Halo: Reach's netcode, manual sync should be used from synching the state of things (if something is destroyed; hp values; etc) while networked events are good for synching the reason for state changes (for example a network event that plays an attack's unique sfx and vfx)

cold laurel
#

Yes, network events are well suited to synchronizing short lasting effects and sounds.

north thistle
#

I've also used events to request the owner of a manually synched object to change the values on that object

cold laurel
#

I find that to be one of the more sane approaches.
For example if I want to send a damage event to someone else's object.

#

I guess what I'm wondering is, are you asking if anyone has tested the fact that standalone Quest users get suspended when they take off their headset?

north thistle
cold laurel
#

Yes, I can confirm that standalone Quest users get suspended when they take off their headset. And afaik the timeout before they get kicked from the instance is currently one minute if they're the instance master, and three minutes otherwise.

warm steeple
#

apparently it's somehow possible that one player can see the other in an instance while others can't, not sure if the player who can see has to be the master or not but that's a thing

#

yesterday when testing my world that happened, didn't know vrc networking can get that bad

#

my friends thought I'm going crazy lol but I recorded it all on video

cold laurel
#

Ah yes, gaslighting has returned

strange token
#

Or could it just have been that

warm steeple
#

not sure since idk the settings or how it looked from my friends' perspective

#

but they didn't even see the portal that user opened

#

the user was also on quest and we were all on PC

heavy spindle
#

Yesterday had a fun thing happen where tried to join off a friend, but got hit with eac saying cannot verify vrc files. Was able to invite them to another world just fine. Restarting didn't fix either

#

I lost the logs otherwise I'd post a canny

#

No sane person would probably want to sift through 3 days of uptime in an idle world logs before hitting the bug anyways

vapid pagoda
#

I would never vrcAevSip

slender horizon
#

Are you not able to use SendCustomNetworkEvent on a script attached to the same gameobject as the event sender?

#

I'm having a ton of issues trying to get that working, among those being this error: Unable to send network event 'LoadMazeData' as it does not exist on the target Udon behaviour 'Maze Generator'.

#

The function is set as [NetworkCallable] public void without arguments

#

and the script sending the event is a UdonSharpEventSender

twin portal
#

are you calling it with [OtherScriptReference].SendCustomNetworkEvent(...)?

slender horizon
#

no, just SendCustomNetworkEvent(NetworkEventTarget.All, nameof(mazeLoader.LoadMazeData));

#

should it be in reference to the other script?

twin portal
#

that would be the problem then

#

if you call it without a reference, then SendCustomNetworkEvent targets the current script

#

This is the same for any of the other CustomEvent functions too

slender horizon
slender horizon
twin portal
slender horizon
ocean blaze
#

I am wanting to generate a random number, that is the same for every player in the instance. however I'm getting stuck with it being different for every player.

is there any documentation on doing this that i could read through? or examples other people have used?

minor gale
# ocean blaze I am wanting to generate a random number, that is the same for every player in t...

seeding would probably be a good idea, you could initially sync the seed and receive the same outputs as a result

https://learn.microsoft.com/en-us/dotnet/api/system.random.-ctor?view=net-9.0#system-random-ctor(system-int32)
(constructing a random instance with a seed)

https://learn.microsoft.com/en-us/dotnet/api/system.random.next?view=net-9.0#system-random-next(system-int32-system-int32)
(using a random instance to generate a next number within a range)

you could also sync the array itself if it's easier and not especially large

ocean blaze
minor gale
#

actually i didn't realize you were just randomizing the index, it'd only necessitate syncing the int length if it's only the length you need to sync

if you're filling it with random elements that you want to align across players seeding would still be relevant though

ocean blaze
#

easy numbers is a hardcoded list of values. i just want to randomise what value i pull out of the array

minor gale
#

ah okay, yeah that'd only necessitate syncing the index; it'd look something like this if you're unfamiliar with how to sync it

[UdonSynced] private int syncedIndex = -1;

// Only run this on the owner
private void OwnerRandomize()
{
  syncedIndex = Random.Range(0, EasyNumbers.Length);
  
  getPuzzle(syncedIndex); // The owner doesn't call OnDeserialization, so we have them enact the logic here
  RequestSerialization();
}

private void getPuzzle(int index)
{
  return EasyNumbers[index];
}

public override void OnDeserialization()
{
  getPuzzle(syncedIndex);
}
ocean blaze
#

ahh, semi working now. now the seed is hard coded it generates the same puzzle each time, but it is network synced. ill have some fun with this to work out what my options are

ocean blaze
#

Woooo, working randomisation:

TY for the help Occala

#

for those who might be looking or interested in randomisation synced across other players so everyone gets the same random number:

cold laurel
ocean blaze
#

Right. Because I don't need it to sync until I actually need it to sync, it doesn't have to be a network'd button.

warm steeple
#

also pls use nameof when calling events

north thistle
#

You can also sync more than just a single random number if you use System.Random and sync a seed

[UdonSynced] private int mapSeed = -1;

GenerateMapFromSeed(mapSeed);
...

public void GenerateMapFromSeed(int mapSeed)
{
        System.Random rng = new System.Random(seed);
        //random map generation using rng
}
drowsy rover
#

Do inactive scripts receive synced variable updates?

My scenario:

  1. There is an initially disabled object containing a script with some synced variables, there are 2 players in instance.
  2. Player#1 (owner) activates an object (gameObject.SetActive)
  3. Player#1 modifies some synced variables in the script's Start() function, calls RequestSerialization()
  4. Player#2 activates the same object a bit later
  5. Player#2 never receives OnDeserialization() event and the values of synced variables remain default, no changes from Player#1 are visible

Is this working as intended?

twin portal
#

inactive scripts do not receive network events

#

you'll want to keep them enabled

drowsy rover
#

oh well

drowsy rover
twin portal
#

yep. by network events I'm also referring to things like OnDeserialization

tulip sphinx
#

@drowsy rover yes. the only way to have networked udon behaviour disabled is to make sure its state is synced for everyone as well through vrc pool or similar system. cant have it on for some and off for tohers and expect it to work later.

drowsy rover
#

I see, thanks

tulip sphinx
#

disable other components of an object or move synced script somewhere outside

drowsy rover
#

Ok, so it turned out I was stupid and didn't actually call RequestSerialization 🤡
And now that I do, I actually get all the changes even if the object is disabled at the moment when those changes were made... Huh?

#

I even get all the intermediate changes, if the variable was changed multiple times. As if all those network updates were queued and processed all at the same time when the object became active

north thistle
#

Disabled and newly enabled Udon Networked Objects have unexpected, likely unpredictable behavior

Like others have already suggested it is generally best for your networked objects to be active at world load and never get disabled, instead using other means to "disable" the object (for example disabling the renderer and/or collider)

drowsy rover
#

Yes, I will keep them active. Just trying to make sense of what is going on.

north thistle
#

It's been awhile since I last tied messing around with disabled network objects, but iirc it's mainly an issue when you try to activate an object and then try to have it sync new value

#

But there might be other weird behavior that will randomly pop up in ways that won't throw any errors

tawny basin
strange token
#

That was the beginning of the end of my last project actually

#

Spent a week or two trying to get udon synced property calls on arrays then switched to strings instead, then my players weren’t syncing up after setting Random’s seed and ended up giving up

#

Too many hurdles and brain scratchers and not enough progress

obtuse echo
# tawny basin In regards to this btw, I've been doing some research into it and I've found onl...

Hmm, I currently use System.Random to do exactly that and it seemed fine to me. Although I have not gotten on a quest and pc at the same time to make absolutely sure. So who knows, it might have different results... I'll have to try it out once the login servers return.

But seems like it's relatively simple to just make your own random number generator if the builtin one doesn't work properly.

tawny basin
#

I see. That did also come up in my reasearch
Making your own engine, that is
I have to wonder why it'd be different anyways at that rate even with System.Random if it's just written as-is the same as making your own

minor gale
north thistle
#

If there are variations it might be very slight. Might be worth making a testing instance where one player produces a long series of ints from a seed, syncs the seed and ints, and then the receiver of the sync also generates a series of ints from the seed and sees if there is any difference

obtuse echo
#

Yeah it seems to work fine cross platform for me too.

#

Seems like floating point errors could cause issues theoretically, but the chance seems low? It's definitely something that should be tested for more serious projects.

minor gale
#

are doubles actually reduced at that level? i thought it was more the rendering or maybe unity level (transform positions), but i'd be sorta surprised personally if floats or doubles in data land were reduced

#

thank you for testing btw

obtuse echo
#

I wouldn't know for sure. All I can do is ask AI™️which claims that the Next Method is actually accurate and that only NextDouble can differ because it multiplies by a floating point constant internally.

#

Which I guess makes sense?

minor gale
#

spooky 👻

surreal mango
#

can cheaters forcibly make themselves instance master and/or object owner? are there ways to do checks and prevent that? in udonsharp

vapid pagoda
strange token
#

Definitely seems like a prevention to me

warm steeple
minor gale
#

that event is just run on clients, the server transferring ownership from a leaver is kinda just implicit, like this doesn't even run afaik, so it's not like it's unclear if you don't denote it imo

strange token
#

That’s what I was thinking too

wintry bear
#

The server's photon layer is primarily where ownership is handled. There's the default behavior which is that the master owns all objects, with hooks and callbacks that can be used to facilitate ownership transfers. I believe generally speaking any client can make an ownership request, and if not rejected either by photon or the Udon ownership request callback, then photon will just inform all clients of the transfer and start regarding changes by the new owner as authoritative. So yeah, someone leaving is just implicitly handled by photon as well.

hybrid kindle
#

so i got a pretty complex and unorganized script and im running into an issue. Can someone tell me where its messing up (and no you don't want to see it😭 )

  • Object synced pickup enables bool in udonbehavior 2 when collide with udonbehavior 1.

  • Player1 puts in one object, player 2 puts in a different one. Two bools.

  • Any player interacts with UB 2 to "try combination" Auto fail every time for everyone.

  • Player 1 puts in same combination and "trys" with success but player2 doesn't get the same result.

  • UB 1 is not synced because thought the object synced items would do it.

  • UB 2 is synced.

#

I'll show a fraction of UB1 to get the idea.

#

This is ub2, the only difference between win and fail is the audioclip. (I know its cursed)

warm steeple
#

the intent is better conveyed to a human

minor gale
#

to play devil's advocate though, imagine someone reads this and thinks it is the reason the ownership transfer happens on leave, like i think understanding the underlying reason is better than injecting something that somewhat conveys that to someone who would need to know the case anyway

#

if it makes it more clear to you that's very valid though

warm steeple
#

hrm true if they don't know when that method is called

strange token
#

Any clue what I'm doing wrong here?

#

Wait, I forgot network callable, testing again

#

YEP that fixed it

faint rock
north thistle
faint rock
#

Would the first be a problem? A few frames it very brief and it'd then snap to the correct postion anyway wouldn't it? I've seen the "snap back" issue but never in one my worlds where I'm doing this so I suspect something else also required for that issue to trigger...

north thistle
#

It's been awhile so I don't remember the specific details, but I'm pretty sure "networking straight up not working" was me trying to change a value and then RequestSerialization() and the updated values will never going out.

thorny nebula
#

so i have a button that turns on and off a animation bool. it works but its only local.. idk what i need to do to make it global for everyone
does anyone know whats the issue?

tulip sphinx
#

replace both sendcustom networked event with just send custom event. Make sure State is marked as synced.

thorny nebula
twin portal
#

Is the State variable set to be synced, and what is the sync type that your UdonBehaviour is currently set to?

thorny nebula
thorny nebula
twin portal
#

That all looks fine then

hoary frost
hybrid kindle
#

ok so- object follow player script. But delayed like a follower. Which node?

twin portal
hybrid kindle
twin portal
#

this method just does the math, you'd then take its output and assign it back to the follower's position

hybrid kindle
#

thank you

twin portal
# hybrid kindle thank you

here's an example, this will make one object follow another position. You could modify this graph slightly to make the player the target instead, if needed

mint fractal
#

Is it possible to sync a DataDictionary?

twin portal
# mint fractal Is it possible to sync a DataDictionary?

kind of. You can convert it to a JSON string, sync that, and deserialize it back into the DataDictionary
https://creators.vrchat.com/worlds/udon/data-containers/data-dictionaries#syncing-a-data-dictionary-with-other-players-over-the-network

Data Dictionaries store Data Tokens by key-value pair, similarly to C# Dictionaries. Most Data Dictionary functions are just wrappers for the underlying C# dictionary, so the C# dictionary documentation also applies if you are looking for more specific details.

minor gale
#

just note that the keys must be strings (this is more of a json limitation from what i understand, not exactly vrc's issue)

and that numbers will be represented as doubles when deserialized from json

mint fractal
#

I have always used strings for keys. Anything else is just weird and non-standardized. Like if you wanna make numbered keys, just do "1" instead of 1 for example.

frozen igloo
mint fractal
frozen igloo
mint fractal
#

also, dealing with large amounts of json data is a pain. (each of these is really messy and long)

frozen igloo
#

I suggest making static functions to navigate through your datatoken structure and pull out the parameters you want into a strict-typed return value, so your working code which does things with that data doesn't have to constantly be casting stuff around

#

You get to design your own interface for the data as you want it - datatokens are just the base container

mint fractal
# frozen igloo I suggest making static functions to navigate through your datatoken structure a...

Also, will support for static fields ever be added? Not sure if that's possible.

Also, could you please take a look at this? I believe this is something that wouldn't require much work (I may be wrong though): https://feedback.vrchat.com/feature-requests/p/discord-activity-proxy-discordsayscom-in-url-allowlist

A lot of cool things are only possible with custom server code, which requires untrusted URLs. I’m assuming the main reason is to prevent IP grabbing.

hybrid kindle
#

whats the best way to sync an int value for late joiners?

mighty steppe
hybrid kindle
mighty steppe
cold laurel
# hybrid kindle whats the best way to sync an int value for late joiners?

Learn how to use Udon to make a slider that shows its value and syncs to everyone else in the world.

0:00 Building a Slider
2:54 Adding a Text Field to the Slider
5:04 Showing Slider Value in Text Field Using Udon
9:52 Syncing the Slider Value over the Network
13:42 Restricting Variable Updates to Owner
14:46 Switching to Manual Sync
16:26 Rest...

▶ Play video
hybrid kindle
frozen igloo
#

Theres a few things you need to do in order to hook into all the right pieces. For something as simple as just a toggle it may feel cumbersome but each part is something important that plays a role in larger systems. In general, the flow is: set up a synced variable and use manual sync. When a user interacts you need to take ownership, set the variable, requestserialization. Then on the receiver side, respond to ondeserialization.

#

The exact way you do all that depends on you, but a good practice is to create your own function which applies the state of the variable onto the world, and then you can call that anywhere to ensure that whatever the variable says, the world is in sync with that. "anywhere" in this case would be after you have called requestserialization and another after receiving ondeserialization

hybrid kindle
frozen igloo
#

That's because you're using a network event to transmit the interact. That's not truly syncing a state, it's just telling everyone around to increase the int by 1. Late joiners dont get old network events

hybrid kindle
frozen igloo
#

Probably didnt have that set up correctly then

hybrid kindle
#

is there a correct way other than this?

frozen igloo
#

You do have selectedmaterial listening for a change, which is great. That is an alternative method which does simplify q bit - you dont need to listen for ondeserialization or call an application method manually

hybrid kindle
frozen igloo
#

Intlogic is the part of your code which changes the value. You dont want late joiners attempting to change the value, you want them listening for and applying the value to the world when they receive it

frozen igloo
#

Which will happen automatically if this is manual sync behavior, synced variable, and not disabled by default

#

I would suggest removing the ondeserialization so that the onvariablechanged can work alone

hybrid kindle
#

I will try that

hybrid kindle
cyan hollow
timber ferry
#

ooh so that's why stuff was respawning weirdly in my world, in which i had changed nothing

#

i noticed stuff was respawning where it last was, not where it started

#

that's so weird

rugged ginkgo
north thistle
#

Well I've found the niche use case where PlayerObject's locked ownership causes issues with my use case.

I want to create a system with two networked elements: a data element and a visual element. The data element will be solely controlled by the player so that is fine, but I want the visual element to be a pickup that other players can pick up. As a result I'll have to create my own implementation of PlayerObject... :/

warm steeple
cyan hollow
north thistle
#

I never ended up using it myself because it lacked the ability to change the respawn point

solar crescent
#

I made this flashlight script and I wonder....it's switching on/off when someone uses it and then playing a click and the animation turns on the light...but I have one weird problem...

#

The instance owner has everything normal, but remote players coming in telling me it clicks nonstop and if they grab it and use it, it stops non-stop clicking, but then clicks for me endlessly. What's causing that?

(and no, I checked, the AudioSource is not on loop)

twin portal
#

don't combine flows, you're sending RequestSerialization and OnDeserialization to the same place, which can break the compiler and cause unintended behavior, possibly could be the cause of your issue

#

separate those nodes into its own function, and have each flow call the function instead

#

also, is your script set to Manual, or Continuous sync?

solar crescent
solar crescent
#

Hm I mean the flashlight click is an event that's needed ONCE in that moment, independent from the state of it. I make it a NetworkEvent and get it out of the networking bool! >->

#

thought about putting the Udon Nodes on an object below the flashlight, but then I'd have 2 boxes...one for the pickup-hold and one for the on/off script. If I wanna one box collider for both scripts, I need to keep the on/off thing continuous, otherwise it moans at me on upload, that I can't on an Object with ObjectSync having a script that's manual =w="

twin portal
# solar crescent Continuous, because it bitches, when I put it to manual, what would be enough. B...

ok I think I know where your bug is coming from then. RequestSerialization and OnDeserialization are meant for Manual sync, but OnDeserialization still fires when you have a script in Continuous sync
This means that, rather than only firing once like OnDeserialization usually does, with Continuous sync it fires several times a second as its variables are continuously synced
hence why the sound was playing over an over for other players. The sending player never runs OnDeserialization

solar crescent
#

whyever the one script is forcing the sync of another one shrugs just 'because'

twin portal
#

it's because scripts on the same GameObject are all network synced together, so they can't have mixed sync types

solar crescent
#

Mhm...having states on synced objects that need to be networked seems to always be a little costy for performance if states of scripts on one object are forced to be the same. Ofc you need continous sync for an ObjectSync...but not for state changes

#

noooooot flexible! ^^

twin portal
#

using a Network Event is good, that's what I was going to suggest for the sound, but for the light you'll probably want to use a synced bool and use its Change event to update the animator

solar crescent
#

So that's kinda the solution :x

#

Would the var Change thing also fire when e new player joins like the OnDeserilization does?

twin portal
#

yes

solar crescent
#

oh convenient, am learning things. A few months ago I did everything with OnPlayerJoin check is master if yes check state and send to all to switch to that state, cuz I didn't know about networking vars and YouTube tutorials told weird stuff x333

#

So whenever someone joined, all scripts on everything for everyone checked multiple things and yelling all through each other. I feel more tidy since I dropped it and use vars xD

twin portal
#

best way to learn is to mess around and come up with solutions like that, ones that make you look back and go "why the hell did I do it that way.... but at least it worked"

solar crescent
#

I have a project....nearly 2 years not looked into...it's a hell cuz it was my very first map back when I knew nothing...I am a bit afraid to come back cuz I can redo most scripts cuz it's bad practice and slow...but I will...not now...I'm fixing my current projects first...but this big building ground...will be tackled, cuz it hurts my soul xD

Can you imagine that I needed a full workday of time just to find out how to make a door be synced among players if it's open or closed? Gosh x'D

twin portal
#

I'm sure we were all at that point somewhere in the past lol

#

what seemed impossibly difficult before is now trivial, a sign that you've learned and gained more skill

solar crescent
#

My first moments of coding kinda:
if yes then no while true

#

My PC: 🔥

cyan hollow
tawdry silo
#

hiiiiiii
so im working on a system for my world wherein each player needs their own thing - so, VRCPlayerObjects is working great for that
But, im having some trouble working out how to get a remote visual copy of the object to appear for remote users, like ah, let's say the playerobject is a hammer, everyone does see their own hammer, but then nobody can see other player's hammers.
during runtime i take a visual-only copy of the hammer and unparent it from the playerobject root, assign the original as a source for a parent constraint so that visual copy stays where that players hammer is, and also it has VRCObjectSync on it. however, still nothing, it seems like, because the object started as a child of the playerobject root, that VRCObjectSync isn't working. am i right? Is this the wrong way to go about this? do I need to script my own system using network events to maintain a visual copy of the hammer in each remote players hand

#

i could only find one video that might be helpful but it uses the visual scripting, i really prefer to stick to manual scripting but some of the scripting api seems lackluster for the vrchat specific components

twin portal
#

on what object specifically is the VRCObjectSync component located? The pole or the hammer?

tawdry silo
#

ah im sorry, by pole i meant hammer

#

heres the heirarchy

#

also i lied its not a hammer its a fishing pole lol

#

so, im taking "visualpole" and unparenting it with visualPole.transform.SetParent(null);

twin portal
#

are you replicating the unparenting for all players?

tawdry silo
#

in play mode it seems to work fine but i cant actually test what the remote player sees until in game

#

ohhhhh hmmmmmm

#

i should put the unparenting in a network event

#

or something like that

#

i guess i just need to have a central script to manage recieving and sending the network events to keep everything where it should be

#

so, theres no reason objectsync wouldnt work as expected with an object that started as a playerobject?

twin portal
#

it would be healthier to sync it with a variable to track its state instead

tawdry silo
#

could you give an example? im very new to udon networking

twin portal
twin portal
tawdry silo
#

tytyty! yes that makes sense

twin portal
#

actually I think there's an example in the docs

#

for any kind of "on or off" or any sort of true/false states, I like to do what's described in the example here

#

you can sync a bool, so in your case whether or not the object is parented.
Normally, you'd have to edit the value, RequestSerialization, then check in OnDeserialization what the value is, which works for some things but then starts to get convoluted

#

so instead you can use FieldChangeCallback. This is the same as using a Change node in Udon Graph.
what this setup does is automatically run some code whenever the value of the variable is changed to a different value
This is especially useful for networked things, because your variables can essentially automatically change stuff without you needing to shove it in OnDeserialization. Even more useful in cases where you need to use Continuous sync, like if you have a script that's on a synced pickup

tawdry silo
#

i see i see hmmmmmmmmmmm

#

ok, i think i can wrap my head around this, barely, thank you

twin portal
#

it does look a bit complicated, but once you get it set up, it's life-changing for networked stuff

#

the reason why you don't what to use network events is because they aren't replicated for late joiners

#

so doing this approach will also automatically work for late joiners too

tawdry silo
#

so maybe something like ```
[UdonSynced, FieldChangeCallback(nameof(SyncedToggle))]
private bool _visualParentState;
public bool VisualParentState
{
set
{

        _visualParentState = value;

visualPole.SetParent(_visualParentState ? null : original)

    }
    get => _visualParentState;
}```

am i on the right track?

twin portal
#

I think the ternary operator uses a colon instead of ||

tawdry silo
#

oh right right right

#

but you got the idea heheh x3

twin portal
#

yeah idea should work, in theory

#

so then in the rest of the code, all you have to is just change the value of VisualParentState. and then this code will take care of the rest

tawdry silo
#

okok

#

even if im setting that value from the script attached to the playerobject? neat neat neat

#

i'll give it a shot

#

so player joins > i set their visual parent state to true in onplayerrestored > another player joins > the unparented object will be unparented for them??

#

somehow i feel like theres a gap of logic there, but i dont know what all is happening under the hood so, idk

#

thank you thank you

twin portal
#

yeah you've got it right

#

this will also run if the value is changed via a network sync. So other players will be sent the networked variables, they'll see "ok this variable has a FieldChangeCallback, so I'm going to run this other code too"

tawdry silo
#

yes yes, that does make sense
its so hard to imagine it in my head though like, OnPlayerRestored happens , then another player joins, it would seem like the code in the first player's OnPlayerRestored would have to happen again

#

its mind boggling in a "surely im doing something else wrong" kind of way

#

ahhhh i get it i think ok, yes i'll report back if it works :3

#

its syncing that variable and the actual stuff isnt happening in onplayerrestored, its happening in the boolean thingy itself

twin portal
#

yeah you can think of it that way

#

the variable's purpose is to track a state, and you've made it so the variable also sets the state too

#

is just sometimes convenient to keep it all in one place

solar crescent
#

The flashlight does the click and changes the bool on click, but the state Change is not firing, not doing anything. Any ideas? w_w

twin portal
#

make sure you check the "sendChange" box in the "Set state" node

solar crescent
#

gosh! You're ma hero, I was biting my pillow already xD

solar crescent
#

I have a variable that will count up if a part of a mission is completed and whoever triggers or interacts to fulfill that, will cause for everyone to make it impossible to cause this again - otherwise for every player on the map it would +1.

But what's if the trigger itself is an object carried into a trigger. Then I can't catch the player entering the trigger but an object that's synced will enter the trigger for everyone.

How can I prevent the int going +1 for each player on the map in that moment? x_x

minor gale
oblique reef
#

I've built out a VRChat world menu builder! It should make creating custom menus for your toggles, settings, and most everything else, with a lot of room to create add-ons and custom versions.

Happy to take any feedback you might have!
https://youtu.be/PJduLxL-sDM

foggy jackal
#

Looks really cool!

tawny basin
#

I know this is very late, but I was reading thru this and got curious
If you're saying things like opening hatches resolve for late joiners, why is there an event for it?

wintry bear
#

Hatch open states don't resolve for late joiners, and the game is actually designed around that. While I could sync an array of 17,576 bytes with 6 of the bits in each byte representing the hatch state on each side of each of the rooms, that's not terribly efficient, especially given how frequently hatches are interacted with by a variety of different people.

Instead, players can only join when a new round starts. Each "round" lasts 3 minutes, and at the end of those 3 minutes all the hatches force close and lock, the rooms do some reshuffling, and the next round starts with the new players being spawned into a starting room and all the hatches unlocking.

This means late-joiners can only ever enter the game with all the hatches in a known closed state. From there I just use the events system for players to indicate to everyone else when a hatch is being opened or closed. This is efficient network-wise (just an int (room id), a byte (direction), and a bool (attempting to open or close), sent to all players), and I've found quite robust in practice.

The only scenario where it could potentially lead to some minor strangeness is in the observation room, where you can watch the current game in progress. If the late joiner goes there before a new round starts, they might observe someone go through a hatch they don't see as open. But this doesn't affect gameplay, and hatches auto-close themselves after a certain cooldown of no one in an adjacent room anyways, so it's still not super common.

minor gale
# wintry bear Hatch open states don't resolve for late joiners, and the game is actually desig...

if you have a way to index into the hatches it'd probably be more practical to only file hatches that are open (putting their index in an array), but you'd need a way to resolve an entry being removed from the local perspective to close it again (or otherwise store their state if they were interacted with at all)

in the worst case that's a lot more data though (idk if it's possible/likely for people to open every hatch)

events are good for round-based things though (so i agree with your handling of it)

#

though with all of them being in one array it'd be a pain to sync often. if it were absolutely necessary to late sync them i'd probably spread them into clusters

wintry bear
# minor gale if you have a way to index into the hatches it'd probably be more practical to o...

At smaller scales I'd just make each room its own networked object and have it manage its own hatch state. But with 17,576 rooms, this approach was far more practical haha.

The rooms still each have their own script that run locally and handle the internal logic, they just communicate upward through a singleton controller that listens for network events, and that then propagates the request back down to the specific room on each client. It's integrated with the dynamic room loading/unloading system as well.

wintry bear
# minor gale though with all of them being in one array it'd be a pain to sync often. if it w...

If I were to late sync them, I would probably just have the master run a function on player join to query all the currently loaded rooms with open hatches, get their hatch states, and bitpack an int array with the data. 15 bits to index the room, and 6 bits to indicate the state of each hatch. Far less than the total number of rooms are loaded up at any given time, so it wouldn't be too expensive an operation to run every so often like that, and it would still keep the size down.

minor gale
warm steeple
#

I wonder if someone has an insight to what's happening with vrc networking rn (the "unusual client behavior" error). people say it's cloudflare but what I observed is the missing object reference in the console. even in my world when I regenerated ids I would get booted from the world right as I join with this exact error, which didn't make sense to me until I realized all networked worlds are affected to varying degrees

#

what are network ids even used for? do they get uploaded to some networking server and photon uses them when communicating? if that is how it works it would make sense why the regenerated ids caused errors, maybe they don't get uploaded due to cloudflare issues

cold laurel
warm steeple
#

hm I wonder why I get an instant error when I regenerate network ids then...

#

it's not exactly easy to debug since all it pretty much says is: "shit happened"

#

previously I thought that putting udon behaviors to childs of other udon behaviors causes this issue, but then I noticed I have such objects in the scene right now and they work fine

#

all the references are also good as far as I see

cold laurel
warm steeple
#

I did that a while back

#

will check once more

warm steeple
cold laurel
#

Interesting

warm steeple
#

oookay this is veeeery peculiar. I have 21 objects with the same 4 serialized components. they are numbered in order and it's all good, but when I regenerate IDs the network utility of course goes down the hierarchy adding these objects in order. and when there is more than 20 (I have 21) it causes that "unusual client behavior" right at the world join. but if I move that network ID of the 21th object to the end of the list, then it's all good.

#

wtf vrchat how does this make sense

warm steeple
#

something must've changed on the backend. for example slashco got broken for no reason with the same error on round start

#

they've mitigated it by regenerating network IDs and it's better now, although some people still report issues, but in my case regenerating made things even worse as I've explained above

terse merlin
#

my world only recently started seeing people disconnect with "Unusual client behaviour" when certain events trigger on certain behaviours

#

it hasn't been updated in at least a month, but only started happening a couple days ago

#

it seems to be triggered by networked events?

#

but only a couple network ids in my world are affected

terse merlin
#

tried regenerating network ids but the same object is affected
can confirm though that the disconnect is happening when a local player fires an event on the behaviour itself

#

its strange because i have 17 other identical scripts in the world and only 1 causes this
(editing later on to say i was able to mitigate it by deleting the broken script and copying another one)

warm steeple
#

very interesting, I'd really like to know the root cause

warm steeple
#

looks like it's fixed now after the latest update

jolly star
#

Im also getting unusual client behavior again in reproducible circumstance (that are sadly very complex and hard to narrow down why exactly to a single cause). We need better ways of logging for why. Couldn’t it at least give us the network id in the log of the object that triggers the client to panic disconnect?

Last time this happened it was a random game object that needed to be deleted and recreated with the exact same components and asset paths (no udon was changed, it was the same asset). I already did network ID troubleshooting and it didn’t help that time. Only just raw recreating the game object fixed it as if it was corrupted internally in unity.

strange token
#

Someone had the same issue with a script too, the script stopped working as expected and they had to delete the old one and copy its code to a new one to beat the error

#

Udon seems to be struggling a bit with false positive errors lately

warm steeple
#

I tried removing serialized udon assets to "clear the cache" so to say because I've had an issue with the serializer before, but that did not work in case of networking

#

so far so good though, I do not see the error anymore after recent update

solar crescent
strange token
hard void
#

deathing. help. something's messing up here.

#

the system should
getting the position reference of an object
turning that into a force applied to the world in reverse, giving the illusion of the world moving around you

but right now
when someone else grabs the control object, it stops referencing the position or something and it all just stays still

#

rotation is still perfectly fine, it's just position

shy dragon
shy dragon
#

Oh I see. It setting the owner of two different objects?

shy dragon
# hard void the system should getting the position reference of an object turning that into ...

There are a few things here that might be the cause.
The main thing is, likely, its not properly requesting ownership or setting ownership of all the appropriate objects? Perhaps?

One thing I want to mention though is, when it comes to simulating motion, I'd recommend using lerp to procedurally animate instead of physics simulations.

The latter is bound to be janky. This is specifically under the condition that you're simulating motion.

If you're actually developing a vehicle that moves, a rigid body will do just fine in this case.

hard void
#

no idea what lerp is

shy dragon
# hard void no idea what lerp is

Its just, a way to move something from A to B over a period of time (t).
this hypothetical 'something' can be anything, typically. It could be a number that you gradually set.

But, in your case, the value would be two vector3 the first one, taking in the transform. The result is movement.

Oh, I almost forgot, transform.translate might also work well.

If you want, I can look into testing it in speghetti for you. 🙂

Lemme know, yo.

shy dragon
# hard void no idea what lerp is

I actually wrote a procedural animation script a while ago if you'd like to use it.

It has a loop mode too.

Its a UdonSharp behavior, but I can teach you how to use it through screenies if you'd like.

hard void
#

yeah, that'd be helpful

shy dragon
#

No problem. Just trying to find it. 🙂

#

Found it. Just trying to wrap it up in a unity package for you.

#

Just writing headers so its easier to understand in the editor.

shy dragon
hard void
#

that would be helpful, thanks

shy dragon
#

And all of a sudden, my mouses right click button isn't working. Ffs. lol.

I'll try to send it, but now I'm struggling to even record.

#

I'll show a short video.

I wanted to explain through voice but my damn audio interface is broken.

#

The 'Requires Interaction' toggle will require the player to press it to launch the animation.

#

Setting 'Is_Full_Animation' toggle and 'Toggle_Mode' makes it automatic in the 'Object_Animation' editor script.

If thats not working, make sure that the enabler referances the Object_Animation script by dragging and droppingit.

Also, on the enabler script, make sure to set the 'Auto_interaction_time' higher than 0. That will also enforce the looping.

I'll have to re-do this system in the future in a more optimal way as I'd much rather handle networking manually instead of continuous.

I only made it continuous as a cheap way to allow animations to sync for now. lol.

#

But yeah, animating is just a matter of adding to the 'Position_To' variable in the editor.

I believe its in world space - not local space. But you can get around that through clever object parenting.

#

It also loops through the values. So, like, you can have different location points and it will circle back around once it reached the last indice.

tawdry sequoia
#

yo what's goin on!!!

#

lmfao

#

the ping is so high

restive cliff
tawdry sequoia
violet mist
#

quick question: does OnPostSerialization() fire once all variables have successfully been set for remote users? or just immediately after sending the data regardless of whether or not any remote users received it?

and if the latter, is result.success literally just "was the owner able to connect to the server to send the data"?

twin portal
#

the latter. yes

violet mist
#

unfortunate, but okay tysm !

twin portal
#

the former is what OnDeserialization is for, when the client has received data and it's ready to use

violet mist
#

and more importantly, the owner doesn't know when to send the next batch of data in a list, since they don't know when everyone has gotten the previous one

twin portal
#

there's no reason the reserialize if the PostSerializaiton reports it as successful. the data will eventually arrive for other players

violet mist
twin portal
#

not for manual sync serializations, especially if you're sending it very quickly

#

you'd only get that reliability with Network Events, which aren't viable if you need this data to be synced for late joiners too

#

you're treading a two generals problem here

violet mist
#

like, in my experience, network events don't queue properly ever, so i don't necessarily trust them

twin portal
#

the network being clogged doesn't mean the data gets thrown away, it just means there's a delay in sending it out as the sender processes their queue

#

network events don't queue properly ever? that sounds like an implementation problem

#

I've had them queue just fine

violet mist
violet mist
#

i suppose i could try using NetworkEvents again for something more normal lol

twin portal
#

you're already hitting the Network Event limit with 16 x 10 events
but this limit is 100 per-second, with a single network event limited to 16KB of data.
so assuming you're under that data limit, 160 events should take only 2 seconds to process
as in, 100 events will be sent immediately, the remaining 60 events will be sent 1 second later

#

oh, but that's if you actually raise the limit. the default is 5 per second

violet mist
violet mist
twin portal
#

yes. they get put in the sender's queue

#

you can see this queue with GetQueuedEvents

violet mist
minor gale
#

the biggest nuance is that you can't request it serialize multiple times prior to it serializing as it will only send the latest state when it goes to serialize

violet mist
violet mist
twin portal
#

if you get two successes from PostSerialization, they'll send in order

#

I was thinking that you'd be calling RequestSerialization too quickly; then they'll get smushed into one Serialization as occala is referring to

violet mist
minor gale
#

i think you got this already, but success doesn't mean it was received, post serialize is called immediately following pre serialize and just means it was serialized and is going out

success doesn't typically fail in any meaningful way related to networking, it can fail if you try to serialize too much data or if the data is invalid in some way

#

with that said it will be received by other clients unless you're dropping packets and/or in the process of disconnecting

violet mist
#

okay, ty guys for the thorough explanations !

next halo
#

So I have a lobby system. What's the proper way to beat raceconditions via networking? The current issue is that the lobby immediately starts, which serialization takes a few ticks to complete on clients, meaning they error out due to missing data.

twin portal
#

if they're erroring due to missing data, then you aren't waiting properly for every player to be ready

next halo
#

I have one client acting as a psuedo-authority currently, the instance owner handles the logic and tells others about it.

#

But I'm unsure as to how to properly 'wait' until everyone's ready.

twin portal
#

there's a lot of ways

#

when you send the signal to start the game, the client can check if they've received the data they need yet. If not, you could SendCustomDelayedEvent and check again a little later, and put them in the game once the data has been detected to have synced

#

so you could have a flag that flips in OnDeserialization or something

#

or you could store a value for every player, a bool that says "yes I'm ready and have the data", once they have the data synced they send an event to the Owner to flip their value; once all bools are flipped, the game is ready to start

#

or they should already have the data in the first place before the host even has the chance to start the game

next halo
#

What method would be good to make a habit of?

twin portal
#

eh.... I think that purely depends on specifically what you're doing, and what fits best

sick gull
#

I have a lobby system.
On game start, the master checks who is in the lobby zone and then tells them to teleport in-game, there is a brief grace period for roles to be distributed to everyone's PlayerObject that is involved in the game

twin portal
#

for me, I'd do something akin to option 3? the game shouldn't depend on if they've recieved a specific network sync yet or not

#

yeah probably some kind of intermediate state would be best

#

you could just Start Game -> fade everyone to black -> send them into the actual game once they have the data they need

next halo
minor gale
#

if you can get away with events for whatever you're sending out, events would be strictly ordered. the data meant to be given prior to start would simply be before the start event, which is much more simple imo

sick gull
#

I know some lobby systems are like an elevator, the doors close after the button is pressed. Now no one can leave or join the zone.

Combined with occalas suggestions, it works pretty well

next halo
twin portal
#

what you're doing there is like, the most prime recipe for Udon networking race conditions

#

Serialization + Network Event

next halo
#

First VRC game, so I'm learning as I go with networking.

#

I expected a racecondition but just laid it out since I didn't have a better answer at the time.

#

Since I had to implement the rest of the logic.

minor gale
#

outside of that you're going to have to wait an arbitrary amount of time or try to infer that everyone's ready somehow

twin portal
#

based on what you've got, it might be easiest to just modify PaperMasterStartGame to put the player in a waiting loop and wait for the serialization to arrive

next halo
#

Ayyy! It all works.

#

Simple timer and fadeout gives plenty of time, everything works just as expected. Tasks assigned, roles assigned, Task Instances tick on clients that they are instantiated on and communicate to other clients what task stage and ID they are, and other clients can infer it all via a method of Task ID and Task Stage.

lament shell
#

Hey all! I'm having an issue using TryingToSpawn on my VRCObjectPool, moving their position and then setting velocity on their rigidbodies.
Objects lag/stutter for a short moments before moving on players who aren't master/owner. Has anyone experienced this problem before or know a fix?

hoary frost
#

theres potentially an overload to remove the lerping i think, or a flag you can set

#

like flag discontinuity or something close to that

minor gale
#

vrc object pool + vrc object sync has quite a few issues, you'd also find that late joiners would probably fail to see the objects in the correct spot if they'd settled prior to their join

foggy jackal
#

I like the term "lerping" 🙂

lament shell
# hoary frost its because of the transform lerping (made up term, idk how its called) I think ...

It seems I'm still having the same issue with this change.
Maybe I'm using it in the wrong place (this code being in OnEnable as I just want it to happen once), or maybe there's a problem with using AddForce in this context?

    sync.FlagDiscontinuity();
    sync.TeleportTo(target.transform);
    rb.AddForce(direction * 10, ForceMode.Impulse);

The objects appear in the correct place without delay, but move with AddForce after a delay and stutter.

Perhaps I need to take things in another direction to account for network delays? Like if the VRCObjectSync's teleportation is instant, perhaps the force on the rigidbody (from other players view) is not?
I also tried using rb.velocity and experienced the same effect.

hoary frost
#

but yeah make sure its the owner of the object that runs this code

lament shell
hoary frost
twin portal
#

are you ensuring that the Owner of the object is the one doing the moving?

lament shell
twin portal
#

have you tried messing with FlagDiscontinuity?

minor gale
#

i think you usually need to keep the gameobject with object sync warm (active) for what you're trying to do, which doesn't play well with vrc's pool. you'd probably need to use a custom pool if you want to fix the underlying issue, or do custom transform sync

#

more specifically a pool that only disables the visuals is probably the easiest, but you'd have to consider how to handle the object sitting around somewhere "inactive" with a rigidbody and collider still on it

twin portal
#

put 'em in a jostler that keeps them constantly moving

minor gale
#

ideally they'd just be kinematic and the collider disabled, but you'd have to ensure you undo that on the owner when pulling it. discontinuity should handle it teleporting to the intended spot from the remote perspective

north thistle
minor gale
north thistle
#

Oh I see what you're saying! Yah they do make a bad combo. In fact combining using vrc object pool on any networked object can result in issues.

hoary frost
#

Wish it would be possible to send a custom event to a singular player instead of sending to all, with the playerID/name, and checking it on arrival.
Feels weird to have to send as many custom events as there are players for specific global interactions haha

#

cant help but feel like its inefficient network calls wise

sick epoch
#

As long as you aren't calling them every frame, those simple checks at the start aren't too bad

hoary frost
#

hoping i wont run into clogging issues, especially with 20-30 players in the instance + a bunch of pickups active

sick epoch
#

Oh yeah that should be fine as long as nothing crazy is happening on the other side

sick epoch
#

I can't speak on that

hoary frost
#

its to spawn a projectile, with a random angle to it

#

and i need them to be synced quite accurately

#

every x seconds, lets say something like 20 projectiles spawn with a random int attached to them

#

actually i quite diverged from the original problem i presented lol, yeah this isnt about sending an event to a singular player anymore

hoary frost
sick epoch
#

I'd say make alt accounts so you can have multiple "players" at once

#

I believe you can open multiple instances at once if you open the exe directly (or so I've heard)

hoary frost
#

havent tried with the exe directly. Atm i can get up to 3 players at once with VCC quick launcher

#

but theres no way im able to launch vrc 20 times on my pc, it would explode lol

sick epoch
#

That's valid

#

As for stress testing, not much ya can do other than get a bunch of people

#

Just work to make it as optimized as you can and call it a day

#

(that's what I do at least lol)

hoary frost
#

guess so yeah.
This specific thing im making is supposed to be a secret so i cant show it to many haha

sick epoch
#

Hmm

#

Well if you're making a synced particle with random angles

#

Why not have one person determine the angles then send them as one big string to be decoded

#

Then it's one network event

#

Then just cycle through and fire really fast

twin portal
#

if they're random, you could just sync the seed and have everyone locally calculate the randomization

hoary frost
hoary frost
sick epoch
#

So true

hoary frost
#

i wasnt using a seed up till now

#

but i should

sick gull
#

In regards to the alt talk.

You can quickly boot multiple profiles.

#

oh wait yall talked about this mb

north thistle
wicked vessel
#

meow im seeking advice for the best way to get owner of an object for syncing its pos/rot
like some playerobjects that attach to their owners, and everyone sees the appropriate object "attached" to the appropriate player (the owner)

#

specifically, a way that works without a networking.getowner() every postlateupdate 😅

tulip sphinx
#

getowner is not networked function, ie it doesnt request anything from server/owner, so i recon it doesnt make much overhead. in order to move object with player you do need an update event anyway

wicked vessel
#

oh thats not networked?

tulip sphinx
#

it just tells you who your client thinks the current owner is

twin portal
#

you could cache the VRCPlayerApi of the owner into a variable when OnOwnershipTransferred fires, and refer to that variable in Update instead

#

I think GetOwner() is still an extern? while reading a variable wouldn't be

#

but yeah, ownership of every object is stored locally, so it doesn't take a network request to check it

wicked vessel
#

ahh that changes everything

#

thank you both

tawny basin
#

What is your guys' standard for getting players from lobby to game when latency exists?
For example, if I have a game that operates on the win condition of "1 player remaining" well, the data has to reach them and back in order to put them into the match from the instance master's perspective, so ..
Is it common just to include a buffer for players that are loading into said match?

EDIT: If you're searching for this in the future, this was the answer I was seeking: #udon-networking message

hoary frost
#

Any idea on how to solve this problem?

#

Ive tried using the latest joiner's playerID as a seed, but it breaks if youre the one rejoining (it calls onplayerjoined on everyone in random order)

hoary frost
twin portal
#

hmm, you'd have to resync the seed periodically

hoary frost
#

makes sense

#

yeah that would probably solve it

twin portal
#

the master, or the owner of the script?

hoary frost
#

sorry owner of the object yeah

twin portal
#

alright yeah

#

I think any form of it resyncing periodically should work, maybe whenever a new player joins, or at a set interval of time

#

actually it wouldn't even need to be networked I don't think. Just when a player joins, just InitState again with the same seed so the sequence starts at the "beginning" again, which should work for your case

hoary frost
#

every time a player rejoins, the same numbers would pop up?

twin portal
#

precisely

#

you could then, on top of that, have the owner generate a new seed every so often and sync it

hoary frost
#

mhm

#

yeah thatll do, thanks legos youre da goat :3

tawny basin
# hoary frost hey matt, not sure if i understand your question

It might be something I'm overthinking

But if I have a scenario ..
Hmm let's say "last player standing" for this example

I want to ensure all players load/sync fully before starting the actual match

Well, that's where the variability starts because now, each "I'm ready" acknowledgement from player to master is tied in with latency

That's where I'm wondering about how others have approached it

Not to mention drops, if they take to long do I make the choice to drop them or just stall out the loading for others, etc

EDIT: If you're searching for this in the future, this was the answer I was seeking: #udon-networking message

hoary frost
#

load/sync what?

tawny basin
#

Anything really
The main crux is that I'd want to ensure they send "I have finished loading" to the master so that the match can then start

hoary frost
#

theres no such thing as "finished loading" in general terms

tawny basin
#

Yea, the issue isn't about what's being loaded tho
The issue is the acknowledgement that I've finished loading and am ready to start the match and how that would work over the variability of networking

Hmm, if I had an easy example it'd be something like Udon Beatsaber

We want players to start at the same time so they must load the track and agree on a future sever time to start at per-client

EDIT: If you're searching for this in the future, this was the answer I was seeking: #udon-networking message

solar crescent
#

When I change a synced variable, shouldn't the Change of that variable also work on everyone else then?

Because it's just doing the Change for the one running into the event...that's so weird...

#

it should just locally for the trigger person change the variable, but change it for everyone, so it's synced and sent the Change, but it's just not working for others...

#

Problem is that the variable seems to change for others, so they can't do it again, but still they don't get the "variable Change > stuff", so not getting the other event + sound x_x

solar crescent
#

OK I tested around...it seems to only work if the Udon is set to CONTINUOUS and not on manually....I thought cuz not much change I need to set them to manual to save computation power if just rarely something changes...but seems in the case of manual stuff I'd need to set it not on variable change, but do a Serialization? So either
• Deserialidation + Manual Synchronization
• variable Change + Continuous Synchronization

solar crescent
#

So it's sending the change, but not dropping the On Changed event, cuz that's just for the person doing the change...how not intuitive...

is it more performant to keep everything manual but request serialization after change and OnDeserialization checking if variable true and firing the event? And if a LateJOiner joins without the request sent while the person is on the map, will the person still get the Change? Cuz variablo Change events seem not to fire for people not actively changing them...

twin portal
solar crescent
#

Mhm I thought the send change checkbox would do the networking automatically when changed, cuz I "sent" the change...and indeed it seemed to send that change to others, cuz the event was then not longer available for them, even tho their value Change didn't fire x3

#

So is it noticably unperformant to use Continuous and the objects with the bool that needs to be delivered? Or would it be way better to keep it manual, but then request serialisation after the Change and use On Deserialization instead of State Change?

tulip sphinx
#

always use manual. its not about performance its just better in every way when applicable

solar crescent
#

So varChange better locally stuff and Deserialization magic for networking magic~

twin portal
#

Change is best for variables that you're using to track some sort of state; the Change event lets you instantly update whatever you need to once that variable changes, don't have to worry about juggling OnDeserialization and also running the code for the sender

#

(as long as that variable is also synced that is)

strange token
#

I started using field change callbacks for non networked events too because it’s nice to have the code for the variable right there so you remember why that variable matters 😂

#

But networking is what turned me onto it in the first place 💜

north thistle
# tulip sphinx always use manual. its not about performance its just better in every way when ...

Continuous does have the advantage of having non-guaranteed delivery which decreases network load on the receiving end. Also it doesn't have the send rate limit that manual sync has. But you're unable to turn it off on the sending end and that's why it is hard to work with...

This is what makes VRC Object Sync the most performant way to sync arbitrary movement, it has all the benefits of continuous but it also shuts itself off when not needed.

strange token
#

For my "end of game" screen

tawny basin
vernal igloo
#

i noticed that networked events can send to just the owner of an object. would this reduce network traffic v sending to everyone.
because if it will, setting up a player object for per person event sending could be good

tulip sphinx
#

cap is applied to out traffic. so doesn't matter if you send to one or to many, its one event going to server

onyx flame
#

Do manually updated networked variables not get sent to new joiners in OnDeserialization unless I actively request a new serialization when they join? I had to kludge a line doing that into OnPlayerJoined to get a bool sent to new joiners.

twin portal
#

it's one of the main points of even using network variables

onyx flame
#

That's what I thought but it didn't seem to be working. I guess I'll do more testing.

timber ferry
#

i only knew about that when it's formatted like:
public int Count => count; or public void SomeMethod() => something.text = "";

strange token
timber ferry
#

pretty sure it's just a shorthand way to write things

strange token
#

Which means… huh?

#

😂

timber ferry
#

like, instead of

public int Count {
  get { return count; }
}

you only need to write
public int Count => count;

#

does the same thing

#

as well as

public void SomeMethod() {
  something.text = "";
}

vs
public void SomeMethod() => something.text = "";

strange token
#

Interesting, so it means “place this single argument in brackets for this element” but it infers based on type

#

Idk how to talk programmer but I’m trying to learn as you can see lmfao

#

Thanks for learning me a thing! 😂

timber ferry
#

i dont know if you can do as much with it in udon but it's at least nice for making some things shorter

strange token
#

Unity’s docs are perfectly fine

#

MS I never fully understand

#

Assumed I need an actual degree to learn all the vocab I’m missing there 😂

timber ferry
#

that's fair

warm steeple
#

yea lambdas are just methods without a name

#

in python there is literally a lambda keyword lol

sick gull
#

Never been a fan of MS docs

#

I have a Drink UdonSharpBehavior,

Some drinks will have a flat alcoholLevel, some drinks will have a modifiable alcoholLevel that is Udonsynced.

Is it smarter to have a class that inherits my initial drink script and changes the alcoholLevel to UdonSynced? Or would I just be fine have Udonsynced on all the drinks, even if the alcoholLevel won't change on all of them?

foggy jackal
#

Yeah, I've often said I like C# as a language, just don't like MS' libraries or docs or influence on it.

north thistle
#

And they seem to try to explain everything as it comes up, rather than separating core aspects from more niche applications.

#

On a side note, lambda expressions are, in my experience, almost entirely unsupported by Udon so you don't need to worry about them. Also lambda expressions are, in my experience, something that is hard to wrap your mind around in a theoretical sense and requires practical application to really understand... which you can't really do in Udon.

heavy spindle
#

Using callbacks as a form of "async" of some sorts would also be pretty cool

crimson flicker
#

If an owner changes a synced variable on a manual synced script and requests serialization, does Event "Variable" Change play with the updated value for all other players when it's received?

#

I've been trying to wrap my head around networking for a long time, but the more I read and watch I feel like I just get more confused.

twin portal
#

Yes

crimson flicker
#

I'd think the answer is yes, thinking on a whim, but I can't find any documentation that confirms this. I will just test it, I'm just on a reading spr- Thank you.

twin portal
#

If you want a more specific definition:
A variable's Change event fires whenever its value is changed to a different value.
This includes if it's changed locally, or if a player gets a new value from the variable being network synced

crimson flicker
#

Amazing. That's more clear than anything I've gotten before.

twin portal
#

Emphasis on the "different value" part btw; it does NOT fire if you set the variable to the same value that it already is

tulip sphinx
crimson flicker
brittle pond
#
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.SDK3.UdonNetworkCalling;  
using VRC.Udon.Common.Interfaces;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class StartGame : UdonSharpBehaviour
{
    public GameObject gameSelector;
    public GameObject buttons;
    [UdonSynced] public bool objectsToggled = false;

    public override void Interact()
    {
        SendCustomNetworkEvent(NetworkEventTarget.Owner,nameof(OwnerToggleVariable));
    }

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

    public void OwnerToggleVariable()
    {
        if (Networking.IsOwner(gameObject))
        {
            objectsToggled = true;
            RequestSerialization();
        }
        UpdateObjects();
    }

    public void UpdateObjects()
    {
        gameSelector.SetActive(!objectsToggled);
        buttons.SetActive(objectsToggled);
    }
}

Hi. If I'm testing a world build with two clients, and they're on the same account, are both clients technically considered the owner?

#

I'm asking because OnDeserialization is not running, even though I'm fairly sure it should be.

#

My thought process is if both clients are considered the owner, then OnDeserialization won't run for the opposite one because it thinks both are the same client in a way

twin portal
brittle pond
twin portal
#

OnDeserialization won't do that for the sending player because it doesn't run for the sending player

#

you call it in OwnerTogglevariable though, which is fine, but it's a bit better to run update functions like that in OnPostSerialization

#

also, the player running OwnerToggleVariable never will not be the owner, since your SendCustomNetworkEvent only targets the Owner

brittle pond
twin portal
#

it won't immediately, but they should get the update once the Owner runs the function

brittle pond
#

Yeah, it's not lol

twin portal
#

I don't see why it wouldn't

brittle pond
#

I truly don't either

twin portal
#

add some Debug Log statements to verify if the functions are successfully running, like in OnDeserialization and in UpdateObjects

brittle pond
twin portal
#

it does

#

you have to open the debug menu UI to see it

brittle pond
twin portal
#

that is normal. as I've explained

brittle pond
#

You did in fact say that

twin portal
#

only the receiving players will run OnDeserialization

brittle pond
#

To clarify what I mean by the sender, I mean the person who presses the button in the first place. Like it doesn't run for the owner, because they're the owner.

#

That is normal?

twin portal
#

the person that presses the button isn't guaranteed to be the owner

brittle pond
#

Yeah

#

Where in the documentation does it say that OnDeserialization doesn't run for the person who sends a network event to the owner?

twin portal
#

a network event is different from a serialization

#

any player can send a network event

#

you've currently configured your SendCustomNetworkEvent so that only the Owner of the script runs that event

brittle pond
#

I have

#

I think trying to explain my logic would lead us in circles. Is there a clear solution you can think of here so I can see what I'm missing?

twin portal
#

that's the thing, I don't see anything obvious that would cause this script to not work.... The owner changes a synced variable, requests serialization, after this happens then other players should run OnDeserialization

brittle pond
#

I completely agree here

#

That's why I'm so confused

twin portal
#

you could use OnPostSerialization to test of the serialization is actually successful

#

but you're syncing a single bool, I don't see why that would be causing problems

#

you've used debug logs to confirm that each player is successfully running the expected functions?

#

you should be seeing the player that presses the button run Interact, then the owner run OwnerToggleVariable and then UpdateObjects, then the other player(s) run OnDeserialization and then UpdateObjects

brittle pond
#

I'll double check real quick

twin portal
#

run OnPostSerialization and have it spit out result.success as well

brittle pond
#

Is this the right way to use OnPostSerialization?

twin portal
#

nope

brittle pond
#

Lol

#

How do I properly use it?

twin portal
#

it's its own event/function, like how you have OnDeserialization

brittle pond
#

also how would I get its return

#

lol

#

please forgive me if I seem like I don't know some concepts as I'm coming into learning c# from gamemaker

twin portal
#

it's public override void OnPostSerialization(SerializationResult result)

#

if your IDE is set up correctly, you should be getting autocomplete suggestions, they'll tell you how to write the function formats for Unity and the VRChat SDK

brittle pond
#

Some things show up, and others don't. It's actually really annoying

#

Also it keeps trying to import things for me and it keeps messing up my code

#

I've tried to fix that but to no avail

#

Also

#

It's telling me that SerializationResult doesn't exist

#

Not sure how to fix that

twin portal
#

add using VRC.Udon.Common;

brittle pond
#

Unfortunately didn't work

#

There is something wrong with my unity

#

Lol

twin portal
#

you're cursed

brittle pond
#

I guess man

#

I'm gonna try to update the sdk and unity

#

Guys

#

Alright calling it a night that's enough of that

brittle pond
#

Okay. I got it to recognize that SerializationResult exists. It never runs the debug log for OnPostSerialization. Meaning somehow RequestSerialization doesn't run.

#

Why in the world is this happening

strange token
brittle pond
# strange token uhh I haven't seen the code and lack a bit of context, but are you taking owners...
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common;
using VRC.SDK3.UdonNetworkCalling;  
using VRC.Udon.Common.Interfaces;


[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class StartGame : UdonSharpBehaviour
{
    public GameObject gameSelector;
    public GameObject buttons;
    [UdonSynced] public bool objectsToggled = false;

    public override void Interact()
    {
        Debug.Log("interact is working");
        SendCustomNetworkEvent(NetworkEventTarget.Owner,nameof(OwnerToggleVariable));
    }

    public override void OnDeserialization()
    {
        Debug.Log("ondeserialization is working");
        UpdateObjects();
    }

    public override void OnPostSerialization(SerializationResult result)
    {
        Debug.Log("onpostserialization is working");
        Debug.Log(result.success);
    }

    public void OwnerToggleVariable()
    {
        Debug.Log("ownertogglevariable is working");
        if (Networking.IsOwner(gameObject))
        {
            Debug.Log("networking is owner");
            objectsToggled = true;
            RequestSerialization();
        }
        UpdateObjects();
    }

    public void UpdateObjects()
    {
        Debug.Log("updateobjects is working");
        gameSelector.SetActive(!objectsToggled);
        buttons.SetActive(objectsToggled);
    }
}

Instead of taking ownership, I'm sending the event to the owner already.

#

But yeah as said RequestSerialization just isn't running

#

And I just checked the owner does in fact run OwnerToggleVariable

strange token
#

because you're only sending the event to owner

#

the custom network event

#

it needs to go to target.all

#

not .owner

#

ohhh wait hmm

#

ok so whoever presses the button it sends a request to the owner who then syncs the change

#

blind moment lol

brittle pond
#

ye lol

strange token
#

yea you still need to set owner to be able to run the event afaik, but I don't work with events too often

#

so before the sendcustomnetworkevent put a Networking.SetOwner(gameobject, localPlayer); (I think that's proper syntax)

twin portal
#

network events do not require being owner

strange token
#

whaaat

#

well the rest of this code seems perfeclty fine

#

that's where I'd start for troubleshooting I guess xD

#

better to try everything cuz who knows, if it works after then you've found a bug to report

twin portal
#

so you're seeing it log "networking is owner", but then don't see it log "onpostserialization is working"?

twin portal
#

and this is happening in-game?

brittle pond
#

indeed

strange token
#

what the gunk

brittle pond
#

in game as in I clicked build and test for two clients

twin portal
#

that makes zero sense

brittle pond
#

yep

#

bug report time

#

I'm trying this on a new project

strange token
#

And I'm dumping that script into my current project xD

twin portal
#

does OnPreSerialization run either

#

this button isn't instantiated, is it?

#

it couldn't be, then the networking event wouldn't work either...

#

...unless it's the owner pressing the button. then the event wouldn't require networking

brittle pond
#

so that doesn't make sense either

brittle pond
strange token
#

here are my logs for both clients

#

seems like post worked for me

cold laurel
strange token
#

FWIW I'm not on the latest SDK

brittle pond
#

This is in a brand new project. I just made a new script and nothing seems out of the ordinary. My unity is cursed.

#

Tried it on the new project. Same issue in game.

#

Even though I haven't tested specifically that OnPostSerialization doesn't work, I can assume as much.

#

Gonna try it with the example world

#

Okay so the buttons you can press and stuff pretty much works

#

I think "become owner" should let you press the "owner" button and increment it but it's not working there for some reason

#

But the rest works

#

Now I've tried to transfer ownership like zon said. It still didn't run RequestSerialization()

#

Now, I've downgraded my SDK to zon's version and it still doesn't work

#

Like guys

#

What are we doing here

twin portal
#

geniunely no idea what's going on

#

Zon demonstrated that the code itself works fine

#

so the issue is likely due to some way you've set up the GameObject

strange token
#

Really wonk that yours doesn’t work

brittle pond
#

incredibly

brittle pond
#

Where should I submit a bug report?

foggy jackal
crimson shadow
#

chat, i keep struggling.
i keep trying to have the list of player requests show up if at least one person requests, but no matter what i do it just seems to not work. what am i doing wrong?


public class PrivateRoomAccessManager : UdonSharpBehaviour
{
    [UdonSynced] public int requestingPlayersCount = 0;
    [UdonSynced] public int[] allRequestingPlayers = new int[80];

    [Header("UI References")]
    public GameObject playerRequestListUI; // the parent ui that houses the content
    public GameObject playerRequestListContent; // content containing all request uis
    public GameObject basePlayerRequestUI; // base UI that contains the username, accept and reject buttons
    public TextMeshProUGUI accessText;

    [Header("Required References")]
    public PrivateRoomManager privateRoomManager;

    [Header("Debug")]
    public bool hasRoomAccess = false; // whether or not the local player has access to the room
    [UdonSynced] public int lastAcceptedPlayerID = -1;
    [UdonSynced] public int lastRejectedPlayerID = -1;

    public void Start()
    {
        UpdateRequestListUI();
    }

    // ... some code here ...

    // gets called via a button click
    public void RequestRoomAccess()
    {
        if (LocalPlayerHasRequested()) return;
        if (hasRoomAccess) return;

        AddLocalPlayerToRequests();
    }

    public void AddLocalPlayerToRequests()
    {
        if (LocalPlayerHasRequested()) return;
        if (!Networking.IsOwner(gameObject)) Networking.SetOwner(Networking.LocalPlayer, gameObject);

        if (requestingPlayersCount + 1 >= 81) // its too big, have to remove the first requested player
        {
            Debug.Log("Number of requests are too big. Removing first request");
            for (int i = 0; i < requestingPlayersCount - 1; i++)
            {
                allRequestingPlayers[i] = allRequestingPlayers[i + 1];
            }
            requestingPlayersCount = 79;
        }
        allRequestingPlayers[requestingPlayersCount++] = Networking.LocalPlayer.playerId;

        RequestSerialization();
        SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(UpdateRequestListUI));
    }

    public bool LocalPlayerHasRequested()
    {
        if (allRequestingPlayers.Length == 0) return false;

        foreach (int playerID in allRequestingPlayers)
        {
            if (playerID == Networking.LocalPlayer.playerId) return true;
        }

        return false;
    }

    public void UpdateRequestListUI()
    {
        if (allRequestingPlayers.Length == 0)
        {
            playerRequestListUI.SetActive(false);
        }
        else
        {
            playerRequestListUI.SetActive(true);
        }

        // clear playerlist
        for (int i = 1; i < playerRequestListContent.transform.childCount; i++)
        {
            Destroy(playerRequestListContent.transform.GetChild(i).gameObject);
        }

        foreach (int playerID in allRequestingPlayers)
        {
            GameObject playerRequestUI = Instantiate(basePlayerRequestUI, playerRequestListContent.transform);

            PlayerRequestEntry entry = playerRequestUI.GetComponent<PlayerRequestEntry>();
            entry.playerId = playerID;

            SetUIUsername(playerRequestUI, IDToUsername(playerID));

            // TODO: move UI down

            playerRequestUI.SetActive(true);
        }
    }

    void SetUIUsername(GameObject ui, string username)
    {
        Transform uiTransform = ui.transform;
        GameObject usernameGameObject = uiTransform.GetChild(0).gameObject;
        TextMeshProUGUI usernameTMPro = usernameGameObject.GetComponent<TextMeshProUGUI>();
        usernameTMPro.text = username;
    }
}
#

wait i just read the docs, int arrays cant be synced

twin portal
#

int arrays can be synced

crimson shadow
#

wait then

#

what am i doing wrong?

#

wait wait wait

#

if (allRequestingPlayers.Length == 0)
man im dtupid

runic otter
#

Hey i have a question. Im making a horror game where the AI entity can move around the map and when he sees you he targets you but im having trouble understanding how to get the AI to do that in Udon but also how to network all of that to make it so a player can see another player getting chased or have the AI target a player than switch to targeting another player. (sorry im sending this on Christmas as well i have just been trying to do this for a few days now)

tulip sphinx
#

unity navmesh enabled on owner/disabled on others+setting target to current target player location+switching owner on some condition+objectsync to send everyone else npc coords, smth like that

#

tho i remember someone had issues with navmesh+objectsync, idk. personally did such ncps only being local.

runic otter
#

ok thanks ill try doing somthing like that but first i need to make this smart ai

grizzled shell
#

hi, a thing that was not clear from the documentation is, are the network callable method synchronized? they say that the event order is guaranteed, and that multiple players sending events at the same time are not, but if player A sends event C and player B sends event C at the same time, does the second C call waits the first execution to finish or they are put on a different thread?

tulip sphinx
#

its whatever arrives first to listener

ember urchin
#

Is there any examples of a vrchat world pvp combat system? Mainly trying to figure out how to properly do damage when an object collides with a player, how would one update that player's health without changing ownership of their health gameobject, or changing ownership properly without accidently removing the ownership from the player when they could be in use of it?

tulip sphinx
#

place generic collider onto each player, since you cannot access their native colliders. then you can easily get collisions with that. damage nowadays probly should be done via networked events with parameters, ie attacker sends "i shot player 5 with a 9mm" (where player id and ammo type are parameters) and player 5 is upon recieving doing the rest ie calculating actual damage and subtracting it from health

#

if you want damage to be based on what attacker sees. i mean, ping is real.

ember urchin
#

i guess i gotta look into network events with parameters, but i also thought you could detect collision with a player's collider,

twin portal
tulip sphinx
#

@ember urchin it is protected object and returns null on generic checks like raycasting/particles

#

and having your collider stuck to a player is like a couple lines of code anyway

#

especially with persistence-brought playerobjects

ember urchin
#

is there any good basic examples of such? My main objective is re-creating the CTF world for learning but i feel like every step forward makes 3 steps erased

twin portal
#

there are very few tutorials besides very basic examples

#

I'd imagine there's at least one for PlayerObjects at least

runic otter
#

hey so like when you build and test the game and you select the 2 client thing. does that make it a so both are in the same synced local world or does it work if for example that other client is another human on another device. im working on syncing stuff globaly in my world and just want so clarification

twin portal
#

They'll load into the same instance (locally)

runic otter
#

ok so im asuming then i have to go in my code and add networking stuff right im aussuming i cant just put on VRC object sync

twin portal
#

VRCObjectSync will only sync position and rotation

#

So if you want to sync more than that, then yes

runic otter
#

ok then i have this door and key system where the key opens the door and you know basic key door lodgic. that changes position of the door when the key is in the lock which means i dont have to do anything with that. but would that matter for something like a flashlight or does having an animation on the door to change position need code for networking. im just trying to understand.

twin portal
#

Animations are not inherently synced

#

So when one player opens the door, they need to tell other players to open it too

runic otter
#

but i can add my own code to avoid this im assuming

#

alright well i just made code that make sure the door lock is networked and made it late joined protected but my main question was answered

dusk shuttle
#

Is using GetProgramVariable on a synced variable something that happens over the network or is that only SetProgramVariable? My world has an insane amount of network bandwidth despite only having 4 synced variables and I'm trying to track down the cause.

twin portal
#

neither of those functions will inherently send data over the network

dusk shuttle
#

Is there documentation somewhere I can look at that goes over what specific cases cause data to be sent/received via the network (specific to udon ofc)? I'm having so much trouble figuring out what's causing my problem

#

I have a good bit of persistence in my world but it makes no sense that everyone is sitting at ~10KB/s sent when I have a save interval of like 15 seconds and it only updates persistent variables if they've been changed...

twin portal
#

but also, with persistence, whenever a player updates their data, that data is then also sent to all players

dusk shuttle
#

So the sent is the amount of data/sec * number of players?

#

Why would all the players need to be sent the persistent data unless they explicitly were using PlayerData.Get?

twin portal
#

just down to the design of it

dusk shuttle
#

Seems silly imo

twin portal
#

if you need a value from another player's persistent data, it'll be available immediately

#

versus, there would be a delay in getting it if it only pulled it when you called Get

#

that would be worse imo

dusk shuttle
#

Is getting a variable through playerdata more efficient than just using a normal synced variable?

#

I don't really see the use case tbh

twin portal
#

I mean, that depends on what you're using it for

dusk shuttle
#

The way I see it, persistent data is only relevant to the player whose data it is

twin portal
#

but it doesn't have to be

dusk shuttle
#

Otherwise you use synced variables

twin portal
#

then it wouldn't be persistent

dusk shuttle
#

Persistent data isn't saved locally right?

#

It's saved to some server on VRChat's end

twin portal
#

right

dusk shuttle
#

I'll have to test what you're saying by joining a near empty instance and a full one

#

Even if what you say is true, there's no way I should be sending over 10KB/s

#

Max players is 16 and recommended is 8 and even with a few it's up at like 10/sec

twin portal
#

oh also, when you save a persistent variable, it sends all of that player's persistent data

#

not just that one variable that you save

dusk shuttle
#

And again, 15 second save interval for like 20-30 KB

twin portal
#

you can use the debug windows to see what scripts are sending what amount of data

dusk shuttle
#

It only shows the player object itself and the one script I have with synced variables

twin portal
#

is the network actually getting clogged though?

#

wait, are you using PlayerObjects that are storing data in PlayerData?

dusk shuttle
#

No I am not using the component

#

I have player objects but I'm using the normal persistence stuff

#

I just read through the player object docs to make sure and it says you need the component on it for it to use the player object persistence

twin portal
#

is there a reason why you aren't using it?

dusk shuttle
#

I've never tested it and I'm not 100% sure how to set it up lol

#

That's the main reason

twin portal
#

you just add the component. that's it. then any synced variables on the object are also saved persistently, automatically

dusk shuttle
#

Right but only 4 of the variables I have are synced

#

And those are changes very infrequently

#

It's definitely not the problem

twin portal
#

are they set to Continuous sync?

dusk shuttle
#

No

#

I use manual sync

twin portal
#

that doesn't sound right for 10KB/s

dusk shuttle
#

That's what I'm saying lol

#

I've looked through my scripts up and down and I can't figure it out

twin portal
#

is it actually causing any networking issues though?

#

pickups lagging? IsClogged showing?

dusk shuttle
#

Right now, no. It does say suffering but there's not much network stuff in the world atm

#

But in the future I plan to add more network stuff

#

For some context, the synced variables are like what pet is equipped and stuff

#

So there's a slight delay in pets changing when people equip different ones and such, but that's such a minor issue

#

I've not tested isClogged because I haven't updated the world just yet but I may mess around with it sometime later

twin portal
#

so you aren't using ObjectSync or anything to update the position of the pets?

dusk shuttle
#

That is fully local

#

Each player individually sets the pet locations based on the owners

#

I try not to use objectsyncs, those things use a lot

twin portal
#

yeah this kbytes in/out are rising significantly faster than I would expect with a manual sync script

#

manual can sync as fast as continuous though. purely depends on how often you are requesting serialization

#

not reached any suffering yet though

dusk shuttle
#

Yeah i almost never RequestSerialization because again the few synced variables I have are very rarely changed

#

It has to be the persistence, that's the only thing I can think of

twin portal
#

possibly

#

you could test by removing it, then uploading the world under a different ID, and test the networking then

dusk shuttle
#

If what you said about all persistent data being sent at once even if you only update 1 variable is true, that would explain my issue

twin portal
#

I'm at 10 KBytes in and not getting any suffering yet though

#

well, suffering is only out anyway

dusk shuttle
#

Because on top of the 15 second save, I also have various other persistent variables updating under very specific circumstances where it's relevant

twin portal
#

yeah, and that will cause it to upload all data

dusk shuttle
#

That's stupid, just gonna say that

#

I might be able to fix it by just putting all my persistent variables in one save event

#

But then people could lose progress and that sucks

#

I can understand why it has to update everything

#

It's the way it's being saved

#

It's efficient for the server's end but limits us quite a lot

twin portal
#

PlayerData is designed to save data that doesn't need to be saved very often, so when it does need to be saved often, the cracks start to show

#

but that's why there is the alternative, saving with PlayerObjects. those are better suited for things that need to be saved often

dusk shuttle
#

Hmm so I should sync all the other things and use player object persistence is what you're saying?

#

How does that differ exactly

twin portal
#

you'd have to figure out what's being saved too often, and move those to being saved by PlayerObjects instead

dusk shuttle
#

Ah yeah that's no problem at all

twin portal
#

as I said, PlayerObjects work by just persistently saving the last networked state, so you have better control of when it saves

#

and you could skip needing to upload all of the PlayerData. just what's on the PlayerObject

dusk shuttle
#

So it doesn't send all variables when it saves one?

#

If so that's the solution to my problem most likely

twin portal
#

I'm not 100% on it but I don't think it sends all PlayerObject's data all at once

#

so if you have 5 objects, and do a network sync on just 1, just that 1 object's data will be sent

frozen igloo
# dusk shuttle So it doesn't send all variables when it saves one?

Universally, networking in VRchat is bundled per network ID. Everything in the same network ID must be sent at the same time, and they can't be split up further. But if you have multiple network IDs, they can be sent independently. Playerdata only has one network ID, but player objects can have be split across many.

wise pawn
#

That makes sense

twin portal
#

neat so my assumption is right

dusk shuttle
dusk shuttle
twin portal
#

best of luck to you

dusk shuttle
#

I'm glad it's an easy fix

twin portal
#

it was funny you mentioned pets and I was like "wait I have a world on my check out list that sounds familiar...." lol

dusk shuttle
#

Haha yeah

vapid pagoda
dusk shuttle
twin portal
dusk shuttle
#

Ahhh

twin portal
#

You probably wanna be using Bobby's tool to safeguard it resetting persistent data

dusk shuttle
#

I don't think I'd ever have a reason to even mess with that stuff so unless it somehow changes on its own it should be fine

twin portal
#

the most common way this happens is, randomly, you'll get an error saying something along the lines of an issue with Network IDs.
The fastest way to fix these problems is by regenerating the Network IDs; this normally doesn't cause problems, but if your world has persistence, it will wipe and reset the IDs, causing any data associated with a PlayerObject be lost, since the Network ID is now different

dusk shuttle
#

That sounds annoying

#

And that error is just random?

twin portal
dusk shuttle
#

Sounds like vrchat should like, do something about that

twin portal
#

again, this all usually is a non-issue if you aren't using persistence, which most world creators aren't

#

you normally never have to care too much about what the IDs are, but for persistence it's important

foggy jackal
#

When you're first building you world, you'll hit this a lot, but once the major work is done you probably won't be changing large parts around much, and so it probably won't be a big deal

#

well if you build like I do, with not a ton of plan up front 🙂

dusk shuttle
#

I've never come across this issue and I've played around with network stuff in a few worlds now

twin portal
#

yeah, and any issues that do arise don't have to be solved by nuking the IDs and regenerating them, you can fix them manually

dusk shuttle
#

This is the furthest I've taken persistence though by far

foggy jackal
#

it's easy to just nuke and regenerate before it's released - after, probably don't 🙂

twin portal
#

it's all just something to be aware of when you have a project that heavily relies on persistence

dusk shuttle
#

Yeah fair enough

twin portal
#

just to be safe, you probably want to take note of what the IDs are for your PlayerObjects

dusk shuttle
#

Will do 100%

dusk shuttle
#

I should have asked this at the time but now that I'm aware of how the network ID thing works, I should probably create multiple PlayerObjects to split up my persistence so it can save in smaller chunks, right? Since if I have all my persistence on the one PlayerObject it's still one network ID and won't solve my problem lol

#

At least based on what I understand from what I've been told here

twin portal
#

correct

dusk shuttle
#

Alright cool ty

strange token
# twin portal correct

I’ve never heard of splitting persistence between scripts, is that a good and performant way to reduce lag from saving large amounts of data without any drawbacks?

twin portal
#

I can't really think of any drawbacks

#

besides it maybe being a bit more work to set up

frozen igloo
tawny basin
#

Can someone actually give more info with the technical details surrounding that?

frozen igloo
# tawny basin Can someone actually give more info with the technical details surrounding that?

The main point is that if you have two pieces of data networked in two separate places, you need to be careful about how you combine it together.

For example, maybe you have a bool on one udonbehaviour and an int on the other udonbehaviour. Let's say the bool enables an object, and the int sets the color of the object. When these variables are synced separately, it would be possible for you to receive the bool first, before the int. This could cause you to enable the object but with a default, incorrect color.

It may only be like that for a couple frames before it receives the int and snaps into the correct color, but the fact that's possible at all is a problem. Consider if this default, incorrect state was something more problematic like game state which kicked off an entire flow that teleported players, it could be a lot more problematic.

The solution to this problem is to either split across logical boundaries that don't conflict, or account for it by joining the data together reliably.

Splitting across logical boundaries means that as long as you're talking about the same object or system, you'd need to ensure that it is synced in the same bundle. For example, two separate objects with their own toggle and color would be fine to split apart, but so long as there is a relationship between the bool and the int (in this case, the color int is dependent on the bool) you would need to keep them synced together.

Joining the data together is another option, which is simply ensuring that you don't apply the state to the world until you are certain that you have received both states. This can be a useful tool when your networking needs get large enough that you must split things apart, but they still must be logically connected. For example, pick one of the two to be the manager - lets say the int manages the state of this object, but it receives additional variables from the udonbehaviour that syncs the bool. In this case, you would store a local variable which tracks whether it has received the bool, and another which stores whether it has received the int. When the other behaviour receives the bool, it passes it on to the manager. The manager stores it, and remembers that it has been stored. When it receives the int, it stores it and remembers that it has been stored. After each of these operations, it additionally checks to make sure that all variables have been received, and it only ever applies the state to the world when all are received.

It is worth mentioning that as far as persistence goes, this is exactly why we have OnPlayerRestored - it fires when everything for that player has been received. That is an example of vrchat joining it together for you, making persistence easier. But that doesn't help for regular networking data, where OnPlayerRestored won't happen

tawny basin
#

Highly appreciate it.
On the topic of using multiple networked objects for persistence and that being more performant(?)
How does that check out?

frozen igloo
# tawny basin Highly appreciate it. On the topic of using multiple networked objects for persi...

Yeah, splitting your data apart can help a lot if it allows you to reduce how much gets serialized at once. If you still end up serializing both at the same time, it's pointless. If you have large data that can be chunked up into separate segments, and each one can be serialized independently, then that can be good savings.

If you have a situation where some data needs to be really big and other data needs to be sent frequently, putting them on one object can conflict because the big data prevents it from sending often. Splitting your big data from your fast data so that they can serialize separately works very well, but this is the situation where you need to be careful about joining the state back together

strange token
#

@sturdy saffron so why hasn’t this been done for argus yet 😂 or is this the only reason the criminal system can work?

#

Or maybe @jaunty ice is better 👀

jaunty ice
#

It has been done already

#

In various forms

#

Most networked is already fragmented on a per object basis to a reasonably fine degree outside of player save data anyways

#

Player save data is annoying because we don’t want the save data itself to be fragmented into parts where some are more updated than others, and with the current version of persistance, having multiple objects and fields of data comes with all sorts of issues that made us not want to use it at all and use the more global dictionary instead to save stuff

#

(Instead if individual playerobjects that is)

strange token
#

Thanks for quick response! :o

jaunty ice
#

haha i saw the ping so was like ill respond

sturdy saffron
#

Was asleep but bocu beat and has already stated what we do at project aincrad for that issue. I am hope once soba becomes a thing maybe VRC Team allow us to have more control over networked objects and more persistence data stuff opens up as soba is meant to be faster then udon. but how knows how fast.

#

our team would love to test it and get our hands on it to break it to bits haha

timber ferry
#

so i'm setting these networked variables in my timer methods, and reading them from my UI controller script using the getter/setter, however remote players are not seeing the image's fill change. why is this? does reading from the thing not work even if it uses a FieldChangeCallback?

Duck Spawn Controller (where the variables get set and serialized)

[UdonSynced, FieldChangeCallback(nameof(TargetTime))] private float targetTime;
[UdonSynced, FieldChangeCallback(nameof(TimeLeft))] private float timeLeft;
[UdonSynced, FieldChangeCallback(nameof(TimerActivated))] private bool timerActivated = true;

public float TargetTime {
    private set => targetTime = value;
    get => targetTime;
}
public float TimeLeft {
    private set {
        timeLeft = value;
        uiController._UpdateTimerText();
    }
    get => timeLeft;
}
public bool TimerActivated {
    private set {
        timerActivated = value;
        uiController._CheckPoolState();
        uiController._UpdateCountText();
    }
    get => timerActivated;
}
public float TimerLength {
    private set => spawnTimerLength = value;
    get => spawnTimerLength;
}

public void SetTimer() {
    // if (!TimerActivated) return;
    TimerActivated = false;

    TargetTime = Time.time + spawnTimerLength;

    RequestSerialization();
    RunTimer();
}

public void RunTimer() {
    TimeLeft = TargetTime - Time.time;
    TimerActivated = TimeLeft < 0;
    RequestSerialization();

    if (TimerActivated) { // Time ran out, run code that should happen at the end of the timer
        pools[PoolIndex].TryToSpawn(spawnLocation.position);

        SetTimer();
    }
    else { // Timer is still going
        bool poolExhausted = pools[PoolIndex].PoolExhausted;

        if (poolExhausted) {
            return;
        }

        SendCustomEventDelayedSeconds(nameof(RunTimer), TimeLeft % 1);
    }
}

public override void OnDeserialization() {
    uiController._UpdateCountText();
}

UIController (where the variables are read)

public void _UpdateCountText() {
    int total = spawnController.Pool.TotalCount;
    countText.text = $"{spawnController.Pool.ActiveCount}/{total}";
    countTextHUD.text = $"{spawnController.Pool.ActiveCount}/{total}";
}

public void _UpdateTimerText() {
    if (complete) {
        timerText.text = "";
        timerTextHUD.text = "";
        return;
    }
    int totalSeconds = Mathf.FloorToInt(spawnController.TimeLeft);
    int minutes = totalSeconds / 60;
    int seconds = totalSeconds % 60;
    timerText.text = $"{minutes}:{seconds:D2}";
    timerTextHUD.text = $"{minutes}:{seconds:D2}";
}

public void _CheckPoolState() {
    complete = spawnController.Pool.PoolExhausted;

    timerFill.enabled = !complete;
    timerComplete.enabled = complete;

    timerFillHUD.enabled = !complete;
    timerCompleteHUD.enabled = complete;
}

private void Update() {
    if (spawnController.TimerActivated || complete) {
        timerFill.fillAmount = 0f;
        timerFillHUD.fillAmount = 0f;
        return;
    }

    float timeLeft = spawnController.TargetTime - Time.time;
    if (timeLeft < 0f) timeLeft = 0f;

    float fill = 1f - (timeLeft / spawnController.TimerLength);
    timerFill.fillAmount = fill;
    timerFillHUD.fillAmount = fill;
}

#

when i test locally with two clients, the timer fill works completely fine. when i test with a friend, they say the timer does not fill at all

#

so it's like when remote players run Update in the UIController, the values from the getter/setter are just not synced at all

#

i've been meaning to get someone to test with me and have their debug console open, but i've yet to get someone for that

#

but, the other thing is, the timer text and count text are perfectly fine, and they also use FieldChangeCallbacks and getter/setters

#

those parts are synced just fine

#

so is it because i'm not doing anything with the time related variables in OnDeserialization or the setters?

north thistle
#

timeLeft having its value updated triggers a method that replies on the complete value to be accurate, but does itself not make sure that complete is accurate; that value seems to be made accurate by the completely separate act of FieldChangeCallback being triggered for timerActivated

#

I'm not sure if it is causing the issues you are encountering, but this design approach seems like it'd be extremely suspectable to errors

strange token
#

From what i understand from phase earlier, synced variable changes are received in the order they’re declared, so just putting the callback on the last declared variable of the bunch might fix it 👀 but in my experience fieldchangecallbacks never notice other udon synced variable changes so I put them in a one frame delayed method

timber ferry
#

yeah, that makes sense. i’ll try not using a callback on everything, and just on like, timer active maybe

#

i just kinda threw callbacks on the variables and saw it work with two local clients and thought it was fine

strange token
#

sigh...

timber ferry
#

if it's just a regular UdonSynced variable, that i set and use RequestSerialization on, if i read that variable from another script using a getter, do remote clients still read the synced value?

north thistle
#

My personal style often has my networked variable and getter variable separate so I can't say for certain, but it should probably work

timber ferry
#

i would think so too, i'll just do it and find out

#

cause regardless, having a FieldChangeCallback for every variable isn't working

timber ferry
#

it's funny how one incorrectly-cased letter can cause something to not work

#

i thought i broke my timer text, but it's because i set timeLeft and not TimeLeft

timber ferry
#

(not the timer, something else)

frozen igloo
# timber ferry if it's just a regular UdonSynced variable, that i set and use RequestSerializat...

There is no separation between the "real" value and the "synced" value except for execution order. There is only one place that the variable is stored, and when you receive a serialization that one place will be updated. If your getter property reads from that one place, it will give you whatever is stored in that variable in that moment.

The confusion here comes from the execution order, because when you are receiving synced variables they will be inserted into the udonbehaviour one at a time. After each one has been inserted, the field change callback is ran. Then it sets the next variable, and it triggers the field change callback for that in turn. Only after all of these variables have been set and their callbacks ran, then does it do OnDeserialization.

The reason why it worked in one situation and not the other is because you created a race condition where it works if one variable is set first, but it doesn't work if that variable is set after. You should not rely on any particular order, as it could change with different compilation or environments.

The reason why ondeserialization will work is because it always fires after all variables have been received and set, at least in the same network ID. That allows you to run some code at a point in time that you can be confident all the synced variables are up to date.

If you are asking if an udonbehaviour can get the synced variables from another udonbehaviour, then the answer is yes - but it will always be grabbing it at a very specific point in time. If that point in time ends up being before the corresponding ondeserialization of the udonbehaviour that the variable belongs to, then you could get an outdated value. It's up to you to account for that however makes sense in your organizational structure

timber ferry
# frozen igloo There is no separation between the "real" value and the "synced" value except fo...

that makes sense. i ended up changing the structure to be non-reliant on FieldChangeCallbacks, and use regular UdonSynced variables instead. new code below

Duck Spawn Controller:

[UdonSynced] private float targetTime;
[UdonSynced, FieldChangeCallback(nameof(TimeLeft))] private float timeLeft;
[UdonSynced] private bool timerActivated = true;

public float TimeLeft {
    private set {
        timeLeft = value;
        UpdateUI();
    }
    get => timeLeft;
}
public float TargetTime => targetTime;
public bool TimerActivated => timerActivated;
public float TimerLength => spawnTimerLength;

#region Spawn Timer
public void SetTimer() {
    if (logs) zLogger.Log(name, "[SetTimer] called", LogColor.Yellow);

    // if (!TimerActivated) return;
    timerActivated = false;
    targetTime = Time.time + spawnTimerLength;

    RequestSerialization();
    RunTimer();
    if (logs) zLogger.Log(name, "[SetTimer] calling RunTimer", LogColor.Orange);
}

public void RunTimer() {
    if (PoolIndex != noDucksIndex) {
        TimeLeft = targetTime - Time.time;
        timerActivated = TimeLeft < 0;
    }
    else {
        TimeLeft = 0f;
        timerActivated = true;
        RequestSerialization();
        return;
    }
    RequestSerialization();

    if (timerActivated) { // Time ran out, run code that should happen at the end of the timer
        pools[PoolIndex].TryToSpawn(spawnLocation.position);

        SetTimer();
    }
    else { // Timer is still going
        bool poolExhausted = pools[PoolIndex].PoolExhausted;

        if (poolExhausted) {
            if (logs) zLogger.Log(name, $"[RunTimer] Pool was exhausted, stopping timer.", LogColor.Yellow, true);
            return;
        }

        SendCustomEventDelayedSeconds(nameof(RunTimer), TimeLeft % 1);
        // if (logs) zLogger.Log(name, $"[RunTimer] {TimeLeft} seconds until next duck.", LogColor.Purple, false);
    }
}
#endregion

public override void OnDeserialization() {
    UpdateUI();
    uiController.UpdatePackButtons();
}

private void UpdateUI() {
    uiController._CheckPoolState();
    uiController._UpdateTimerText();
    uiController._UpdateCountText();
}

UIController:

#region Duck Timer
public void _UpdateCountText() {
    int total = spawnController.Pool.TotalCount;
    countText.text = $"{spawnController.Pool.ActiveCount}/{total}";
    countTextHUD.text = $"{spawnController.Pool.ActiveCount}/{total}";
}

public void _UpdateTimerText() {
    if (complete) {
        timerText.text = string.Empty;
        timerTextHUD.text = string.Empty;
        return;
    }
    int totalSeconds = Mathf.FloorToInt(spawnController.TimeLeft);
    int minutes = totalSeconds / 60;
    int seconds = totalSeconds % 60;
    timerText.text = $"{minutes}:{seconds:D2}";
    timerTextHUD.text = $"{minutes}:{seconds:D2}";
}

public void _CheckPoolState() {
    complete = spawnController.Pool.PoolExhausted;
    if (logs) zLogger.Log(name, $"[CheckPoolState] complete = {complete}", LogColor.Orange);

    timerFill.enabled = !complete;
    timerComplete.enabled = complete;

    timerFillHUD.enabled = !complete;
    timerCompleteHUD.enabled = complete;
}

private void Update() {
    if (spawnController.TimerActivated || complete) {
        if (logs) zLogger.Log(name, $"[Update] Returning. Conditions: TimerActive = {spawnController.TimerActivated}, complete = {complete}", LogColor.Yellow);
        timerFill.fillAmount = 0f;
        timerFillHUD.fillAmount = 0f;
        return;
    }

    float timeLeft = spawnController.TargetTime - Time.time;
    if (timeLeft < 0f) timeLeft = 0f;

    float fill = 1f - (timeLeft / spawnController.TimerLength);
    timerFill.fillAmount = fill;
    timerFillHUD.fillAmount = fill;
    if (logs) zLogger.Log(name, $"[Update End] timeLeft = {timeLeft} | targetTime = {spawnController.TargetTime} | fill = {fill}", LogColor.Blue);
}
#endregion
frozen igloo
#

Generally, best practice: only use field change callback when the thing you want to do is dependent on that singular variable, without any outside reference. If you need to depend on multiple synced variables to apply them to the world, use ondeserialization

timber ferry
#

the new code feels better and still works with two local clients, i'll just have to try with other people

#

and with that info, it seems i'm doing it better now because i'm using OnDeserialization instead of the FieldChangeCallbacks

#

basically batch applying the changes instead of individually

strange token
#

Iron sharpens iron! :D

#

the best part of being in this server

timber ferry
#

i was having an issue where:

  • i reset my object pools which share some of the same objects
  • the local client did it in the right order
  • the remote players would reset one pool before the other, causing the states to be all enabled, then all disabled because the old pool got reset to all disabled after the new pool
#

but i put the resetting of the pool in one half, then the spawning of the first object in the other half separated by 2 frames, and then the remote client did it in the right order for once

strange token
#

I'm in the process of trying to wrap my head around some networking for ball physics here and I want some help...

Essentially, when the player swings their "weapon" into the ball, it should calculate a new angle for the ball and sync these changes so other players know the ball was hit and needs to update its trajectory to the new info, but I'm assuming that just throwing these in an UdonSynced FieldChangeCallback property wouldn't work because the hitter will see the ball fling way sooner than other players will due to network delay, which will cause some desync since players are essentially chasing something that's at a different position than the last player to hit the ball... I'm trying to research how to make up for this but I'm struggling where to start e.e

#

I want to add a hitlag that freezes the hitter for a moment & increases based on ball speed... is there a variable I can use to check your network delay so I can reduce the hitlag by that amount when you receive the network update?

#

That would sync it, but I haven't been able to even fully formulate that idea till I wrote the question here xD

#

Time.realTimeSinceStartup - SimulationTime(player), got it :D

north thistle
timber ferry
#

i was just doing them too quickly together i think

tulip sphinx
#

i dont get the whole concept of multiple pools sharing the same object, like what

#

two cars sharing some wheels sounds weird

timber ferry
#

it’s like, i have packs of ducks, and the object pools are subsets of those ducks. so i have one for all of them, one for just the basic ones, one for custom ducks, etc

#

so some of them share the same ducks, and the “all ducks” one shares all of them with the other pools

#

the system works fine, as long as the previous pool resets before the new one spawns the first duck

timber ferry
#

i don’t understand that because any other time i’ve tested my networked scripts, the behavior with local clients was the same as testing with other players in-game

#

makes it really annoying to debug, because i haveto get other people to test with me, so i’m unable to fix things as often as i could if the behavior was just consistent with my local tests

#

i suppose instead of multiple pools, i could give each duck tags for which pack it’s part of (with a script), then have my object pool only spawn ducks with tags based on whichever pool is selected

#

but again, the system i have works just fine in unity and in local tests, it’s only with other players that it breaks, so i can’t even tell why

timber ferry
#

(i fixed my other issue with the object pools)

foggy jackal
#

seems like you have ducks everywhere

timber ferry
#

it's probably the knife one causing issues

#

i don't know who gave him a knife

timber ferry
#

time is different

#

on each client

#

time.time is not the same but target time is

#

no wonder

#

well shoot how do i make that work then?

tulip sphinx
#

use server time

#

time.time just returns client running time

strange token
#

I forget- does requesting serialization on an object also automatically serialize all its child objects' variables too?

strange token
#

anybody? ;w;

strange token
#

Thanks!!

normal sonnet
#

Selfishly transferring huge (260x260) images over udon as Color32 array...

The max manual sync limit is fine... but the b/s hurts

Any techniques I should know to get more traffic? or tricks I can use to pack a bunch of Color32's into a float or something?

timber ferry
tulip sphinx
#

@timber ferry well ye it can be anything but its the same for everyone in instance. so if you need smth to expire in 1000 second you just add 1000 to current time 🤷‍♂️

timber ferry
#

actually, i think i understood what occala’s message meant right as i sent that

#

save the server time in a synced variable on start for the owner, then use that calculate server delta time method when i need to access it

tulip sphinx
#

if you need past events/time passed then ye

fallow mountain
#

You dont need to sync server time, because every client already has access to it via Networking.GetServerTimeInSeconds()

timber ferry
#

yeah but i can’t just use that directly as a replacement in my code, because replacing Time.time with GetServerTimeInSeconds does not work

#

i have to do something else with it to make it work with my current code, but i’m not sure what exactly

strange token
#

It gives the current client’s calculated network delay in ms

#

That may help here? 👀

#

I’m subtracting that from a synced time for each remote client so they properly “release” at the same time as the person that networked it

#

(This is also untested but I swear it makes so much sense in my head, that’s gotta be how you use it?)

timber ferry
#

time and networking are hard

#

what i'm doing now is:

Spawn Controller [Set Timer]:
targetTime = Time.time + spawnTimerLength;

UIController [Update]:
float timeLeft = spawnController.TargetTime - Time.time;

float fill = 1f - (timeLeft / spawnController.TimerLength);
timerFill.fillAmount = fill;
#

targetTime is synced, but Update runs for everyone locally

#

so maybe instead of using Time.time in the spawn controller, i use the server time on both?

foggy jackal
#

seems easy enough to use server time there, yeah

timber ferry
#

would it still work if it's negative too? i suppose i can use Mathf.Abs if not

foggy jackal
#

I'd think that "time left" would always be >= 0

timber ferry
#

i suppose i'll try replacing it with server time when i get home and see what happens then

normal sonnet
north thistle
#

I haven't gone through all your code, but not sure why you'd sync both time left and target time; shouldn't you be able to use one to determine the other?

timber ferry
#

time left is synced for the clock which displays the time left in minutes/seconds, whereas targetTime is used for the image component’s fill

#

i synced timeLeft separately because the clock only updates once a second and the fill updates every frame

north thistle
#

are these two variables entirely unrelated to each other?

#

what happens locally every frame shouldn't matter for a networked sync value, unless you're synching that value every frame which manual sync does not really support

timber ferry
#

well, i didn’t know how else i could calculate the time left every frame for a smooth animation from 0-1 fill using the timer variables from my spawn controller script, besides reading the one synced value as a reference point for the time calculation. and it made sense to me, because i’m reading the time left in seconds directly to display it on text, and i’m reading the target time separately in update to figure out the fill

#

if there’s a better way to display the time left as a clock on text, and smoothly as fill on an image component, i’d be willing to try it out that way, but this is just the way i thought of doing it and what worked (until it needed syncing)

north thistle
#

Well whenever it comes time in your program to update the visual clock, are you not able to look at the targetTime variable and determine the time left to display from that?

timber ferry
#

oh i see what you mean

#

because if i’m doing targettime - server time to get time left anyway, i could just do that to update the clock

#

well, i guess i’m doing Time.time in my RunTimer method, but i get what you mean i think

north thistle
#

as an example, if I wanted to sync a timer that would end at almost the exact same time in realtime for everyone regardless of latency I'd likely do it something like this:


public void OwnerStartTimer(float time)
{
    ownerStartTimerTime = Time.realtimeSinceStartup;
    syncTimerDuration = time;
    StartTimer(time);
    RequestSerialization();
}


public void StartTimer(float time)
{
    targetTime = Time.realtimeSinceStartup + time;
    //do active timer stuff
}

public void OnPreSerialization()
{
    syncTimerDuration = syncTimerDuration - (Time.realtimeSinceStartup - ownerStartTimerTime);
}

public override void OnDeserialization(DeserializationResult result)
{
    float adjustedTime = syncTimerDuration - (Time.realtimeSinceStartup - sendTime);
    StartTimer(adjustedTime);
}
timber ferry
#

and i also made timeLeft non-synced, and just calculate the time left in the UpdateTimerText method, which also works
int totalSeconds = Mathf.FloorToInt(spawnController.TargetTime - (float)Networking.GetServerTimeInSeconds());

tulip sphinx
cedar crescent
normal sonnet
tulip sphinx
#

ye, 4:2:0

cedar crescent
normal sonnet
#

Its so cruel how limiting things are at low sizes... I thought about some smart ways of fitting two pixels into one byte... But if you cut 255 in half you only get two sets of 16?! Rude.

tulip sphinx
tawdry sequoia
#

i have alot of scripts going on

#

how do i stop certain scripts to stop sending data to the network?

#

put them to "sleep"

strange token
#

If the script doesn’t need to network at all, you can set the drop down for sync mode to “none”

tawdry sequoia
#

well i need them to activate eventually

#

i have a hundred ai

#

and well they occupy stuff over then network

#

128b

#

how do i stop them from doing that?

#

is there just a theoretical hard cap to how much networking stuff you can put on the network?

twin portal
#

It's not theoretical, there is a limit

tawdry sequoia
#

i see that now. you can't just "turn off the script"

#

wow. great discovery.

twin portal
#

If it's continuous sync, no

#

The limit for outgoing data is about 11kb/s

#

If you've got a hundred AI agents, they probably shouldn't be continuous sync, or should be sending data out less often if manual sync

tawdry sequoia
#

I was thhinking the same thing...

#

Thank you.

tawdry sequoia
twin portal
#

For continuous sync, you can't

tawdry sequoia
#

oh yeah talking abt manual