#udon-networking

1 messages · Page 21 of 1

twin portal
#

kinda have to do it before the loop

scenic sky
#

ye

twin portal
#

unless you want to wait for the infinite loop to end before checking

scenic sky
#

so.. i need to compare both lists

#

while one is api and the other is int

#

😬

twin portal
#

hmm...

#

what if you just counted how many admins there are. and if the admin count equals the total player count, then it's kind of implied that all players are admins

scenic sky
#

my thought process is some kind of loop to grab the player ids from the api list and compare it to the ints inside the datalis

scenic sky
twin portal
#

I think that would be up to your adding and removing to the list being reliable

scenic sky
#

so like this?

twin portal
#

yea kinda

#

that and maybe editing the button to say "List Empty" or other related fields

scenic sky
#

i believe i should add some feedback logic and maybe reset the index

twin portal
#

sounds like a good idea to me

scenic sky
#

alright ill try it

#

gonna test it now

#

dang

#

it broke

#

i think a infinite loop happened

twin portal
#

awesomeee

scenic sky
#

got a VM error

#

unity froze for like 10 secs

twin portal
#

did you see it log "No More Staff to Add"?

scenic sky
#

lemme check again

twin portal
#

yea it def went through the infinite loop, that's usually how it behaves

#

it'll hang for a bit and go "welp you're clearly an infinite loop. zap" and crashes the script

scenic sky
#

added all 3 players and saw no more staff to add but the ui isnt consisnt with it

#

well

#

sorta

#

i see empty!

#

interesting

#

this time

#

it wont crash

#

but...

#

its being weird

#

its pasting the same name

twin portal
#

ah. because that's the player at index 0

#

which we told it to set the index to whenever the list is full

scenic sky
#

also, it removes the names from the staff list as intended but not from the player list. (UI) wise

#

i also gotta update the button UI each time something is added or removed

#

because itll show empty or previous thing

twin portal
#

wasn't the plan not to display players in the right-side list if they are an admin?

scenic sky
#

yes

#

it should take them off that UI if they are staff

#

i think i see why the duped names is happen

#

gonna dig into it and see

#

welp

#

iv just screwed up a lot :/

twin portal
#

best way to learn is to screw stuff up

#

what's on fire

scenic sky
#

i did some changes to ideally fix the UI problem

#

and its working better because it actually shows the right name

twin portal
#

heyo
not bad

scenic sky
#

but

#

if i use the staff arrow

#

and move it over say once

#

it removes 2 names then gets an out of bounds error

#

if i dont touch the arrow it doesntb reak

twin portal
#

well that's good. tells us precisely where the issue is

scenic sky
#

yeah there is a mild issue too

#

with the arrows on the playerbutton side

#

where it doesnt appear to fully update

#

when i add max staff and press the right player button i get out of bounds

#

hm

#
 public void RightArrow()
    {
        if (playersList.Length == staffList.Count)
        {
            playerButtonUI.text = "Empty!";
            currentPlayerIndex = 0;
            Debug.Log("Right Arrow Cannot continue (No more players to add!)");
        }

        bool isStaff = false;
        VRCPlayerApi selectedPlayer;
        do
        {
            //When triggered we will move the index forward once
            currentPlayerIndex++;
            if (currentPlayerIndex >= playersList.Length)
            {
                currentPlayerIndex = 0;
            }

            selectedPlayer = playersList[currentPlayerIndex];

            if (staffList.Contains(selectedPlayer.playerId))
            {
                //If the player is staff, cycle once more and keep the loop going
                currentPlayerIndex++;      
                UpdatePlayerButtonUI();
                isStaff = true;
            }

            else
            {
                if (currentPlayerIndex >= playersList.Length)
                {
                    currentPlayerIndex = 0;
                }
                //if the player is not staff end the loop
                UpdatePlayerButtonUI();
                isStaff = false;
            }         
        }
           while (isStaff);
    }
twin portal
#

did you remove return from the first check?

scenic sky
#

it would seem i did

#

that explains alot

brisk magnet
#

having trouble finding the pre release option for udon I could swore I saw it a few days ago in the UI but now that im looking for it I dont see it

scenic sky
twin portal
brisk magnet
#

ty

scenic sky
#

gonna take a break so i can focus better

#

its the current version of my script

#

its got a lot of issues rn

#

out of bounds error
somehow trigger an infinite loop
sometimes the player button isnt updating even though i moved the player over (thats usually when it crashes)

brisk magnet
foggy jackal
#

all clients, it's in the world

twin portal
#

there is no beta client for this new stuff, it works on live

brisk magnet
#

ok perfect just double checking beacuse ive been on other platforms where they push beta clients along with the beta sdk

twin portal
#

sometimes VRC does that too, but not this time

#

persistence was its own beta, for example

brisk magnet
#

that one makes more sence its kinda big

#

in these new custom network events can we idenfity the caller without passing their id as a param?

twin portal
brisk magnet
#

oh I see

#

well that saves another 4 bytes

#

Would also be cool if in a future update we could make sync groups. so for objects with a ton of data that dont need to sync together. So we can sync a targetted set of values.

for example syntax could be somthing akin to

[UdonSynced, group = 1] int myValue;

RequestSerialization(1);

twin portal
#

I guess you could just break that up into different scripts and just RequestSerialization on whatever group you need to sync

brisk magnet
#

yea what I currently do, but it introduces an extra layer of engineering you wouldint otherwise have to worry about. making sure timings are right during the cases when you sync all groups etc

#

I wouldint call it a must have feature but it would speed allot of things up for sure

#

also just woundering what happens if you give a network callable a return type other then void. will it send the return as a reply?

#

beacuse that would be kinda cool

twin portal
#

no, that isn't allowed

brisk magnet
#

yea thought so

languid berry
#

finally being able to send arguments though is pretty big

#

i can already think of a way to ditch using synced variables

#

for some use-cases, not all, but low-frequency non-important values could be done without syncing now

twin portal
#

well, you can't fully ditch them

#

as the concept of an Event is still the same; this is a one-time event that all players currently in the instance should get, and not apply to late joiners

languid berry
#

yeah, it's not a replacement

#

but i can think of a few places in my scripts i've wanted this feature

#

that and finally eventarget.others

twin portal
#

lots of cool stuff besides event parameters that I wasn't expecting to see

languid berry
#

yeah, just out of the blue

#

boi i can jsut imagine how esy it'll be to shoot yourself in the foot with the rate limiting and possible out of order events, lol

twin portal
#

the event order is guaranteed

#

rate limit, a bit unavoidable, but should you really be sending more than 100 events per second?

languid berry
#

i can forsee people using it in an update loop with no protection

twin portal
#

well, it's going to be very obvious when syncing starts to slow down because of it lol

brisk magnet
#

I wouldint ditch them dosint make sence for me to do so but, I have a particular use case these can be good for

#

Lets say im calling one of these new network events on object A but I want the event to go to a differant script on Object B. I allready can do this with the no paramater events by wraping the call. but how would I go about this with the new system.

languid berry
#

previously, you needed two seperate methods, and you'd do the logic before you send

#

now you can have one method, and do the logic after you send

#

which is much mroe useful when it comes to timing

brisk magnet
#

what do I have wrong so far here?

frozen igloo
brisk magnet
#

Not sure what matches an IUdoneventReceiver

frozen igloo
#

oh, I see where you got that from - docs usage of NetworkCalling.SendCustomNetworkEvent is wrong. If you use that version, you have to provide an udonbehaviour casted to IUdonEventReceiver like this: (IUdonEventReceiver)this which the docs don't explain.

If you instead just remove the NetworkCalling part and call SendCustomNetworkEvent directly, then that runs in the context of the udonbehaviour this code is running on so it doesn't need that part

languid berry
#

lol, par for the course for the documentation

brisk magnet
#

Idealy id like the call to go cross script like this but I just wanted to get the syntax right in the more simple case first to see how it works

frozen igloo
#

either way, need to specify where it goes

#

The only reason
SendCustomNetworkEvent(NetworkEventTarget.All, ...

works without specification is because that function inherently operates on this so it's already within the right context. But the version inside of NetworkCalling is static so it doesn't have that context

brisk magnet
#

ah I see this appears to be what I needed. for my context

state.SendCustomNetworkEvent(NetworkEventTarget.Owner, "AttackerPlayCard", cardRef.id);

#

Is there a documentation page better then the one I was going off of that I can use for future referance or is it still wip?

frozen igloo
#

that documentation page is the best source of truth. It's quite extensive, but that looks like just a minor slip

brisk magnet
#

fair enough and it does make perfect sence after doing it once

#

honestly happy this syntax is allot better then what I saw in the doc

#

holy hek I just finished transplanting my first function to this new method and the response time is noticibly faster

#

before there was like this 100ms delay between click and action. even on local sim. now theres zero noticable delay

frozen igloo
#

oh, were you waiting until OnPreserialization to apply synced data that you're sending? You don't need to, you know

#

you can apply sent variables immediately

brisk magnet
#

prior I would request serlization on one side and have the server send one back from the other side

#

so I imagine the 100ms was that ping pong

#

theres still a return sync but it seems to start sooner

#

Client >> netoworkEvent >> tableMaster >> sync back to all clients on a differant object

#

thats what the new flow is now

frozen igloo
#

could make it instant if you do rollback networking hehe

#

that's up to your implementation though

brisk magnet
#

Im not familiar with the termanlogy but I may have done it before. im bad with the names of things. like for example I didint know I was doing somthing called "lock step" before but ive done it plenty of times.

languid berry
#

eseentially, you let the client do whatever they want, and it applies locally isntantly, send to the server and sync, if the client's value has to change then you do that, if not, everything is good

brisk magnet
#

oh yea thats a great idea

#

in my case i can just let the server blindly over write what the client does beacuse there will be no change if its the same anyway

#

for me to effectivly do that visualy I dont even need to modify the values theres just one function I need to call that would represent the action visualy but not change the data

frozen igloo
#

Rollback can get super complicated for games involving positional data and hitboxes, but you're doing a card game so you don't have to worry about any of that. Just need a robust event record and undo system

brisk magnet
#

I just need to worry about what slot the card lands on

#

its just a list of positions in memory

#

I just realized how much pain these events are going to save me beyond that one edge case too

#

I was going to use network events to send players notifications now instead of making dozens of them I can just have paramaters

brisk magnet
#

so far initial testing shows im not hitting the bug Ive been getting all week anymore.

but ima need to test at least 100 times to be sure

#

is it againts TOS to write a python bot to play my own game?

#

if I do it with computer vision and not touch the client

twin portal
#

you could do it through sending OSC events

brisk magnet
#

oh ok nvm no need for that much testing the bug still exists exactly as it did before but now im getting actual useable info in the logs when it happens

hoary frost
#

i thought public methods were always callable anyway

brisk magnet
#

ok yea now I have way more clues as to whats going wrong in my project. The network call was not the issue but it was some how blocking the logs I needed to see

hoary frost
#

oooh nice

#

didnt know they already released in beta

brisk magnet
#

im getting outputs for everything I wasint before

#

im getting so many logs now im gona have to de clutter them now lol

#

this is all good news tho

twin portal
# hoary frost what does the networkcallable attribute do?

this new attribute has been sorta teased that it was gonna happen for a bit, though I wasn't expecting it to be a thing until Soba
Instead of "everything public by default", they're moving to the system kind of being the opposite, stuff can't go over the network by default. This attribute now lets you create public functions, that can also not be network-callable
previously you'd just add an underscore to stop it from being network callable, this way seems a bit neater to me

brisk magnet
#

whats the timeline for soba and is it still going to be the c# interface?

hoary frost
#

i wanna have the arguments events asap so i was gonna upgrade, but that may break my existing stuff

#

also where can you see the sdk patch notes, i cant find it

twin portal
# hoary frost so wait on the current sdk beta you cant call public methods over the network by...

with Udon you still can though, it isn't required; public methods are still callable, but "it's not recommended" to use
here's an explanation for it
https://vrc-beta-docs.netlify.app/worlds/udon/networking/events/#legacy-events-and-security

Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.

hoary frost
#

noice

#

thanks

twin portal
#

essentially, the "old" way of calling events are now considered "legacy events", and will still work as long as they are parameter-less

hoary frost
#

kk

brisk magnet
#

the rabbit hole I went down beacuse this log never got to see the light of day XD

errant lantern
#

Can you see the cards of other players ?

scenic sky
#

Hey @twin portal I managed to get the script to an almost passable state, but.. there a couple issues

  1. (minor issue), sometimes a player who was moved to staff will linger on the player button ui until it is updated then itll realize they are gone
  2. (major issue) nothing is syncing over the network, as things are right now. everything is only working locally

do you or anyone else have any ideas why? also if you see any funky stuff in here let me know, i used a bit of AI to help with the UI issues

twin portal
#

well we haven't added anything network related yet, right

#

so that would explain it not working on that part

#

the first one can be a timing thing. Maybe the lists need to just check if they've been updated every few seconds, or just have it update when you click the arrows maybe

scenic sky
#

hmm.. yeah

scenic sky
twin portal
#

I don't remember doing that

#

oh wait right the JSON stuff

scenic sky
#

yeah

twin portal
#

I don't see any variables with [UdonSynced] on it

scenic sky
#

i think its maybe something to do with json not being udon synced maybe?

#

maybe because its private?

twin portal
#

it can be private and still be networked

scenic sky
#

does it need a udon sync?

twin portal
#

yes

scenic sky
#

ah

#

i didnt add it because the example we looked at didnt have it

twin portal
#

also, add [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] right above the class declaration, the public class PlayerListHandler : UdonSharpBehaviour line

scenic sky
#

oki

twin portal
#

_json does have [UdonSynced] on it in the example

#

but it's in the line above

scenic sky
scenic sky
#

xd

#

im so used to things being on the same line yk?

twin portal
#

yeah for variables I like to put those attributes on the same line

#

most of the time

scenic sky
#

alright, im gonna test it rq

#

(i want this to be done and working already, im starting to die inside)

twin portal
#

prolly still have a ways to go

scenic sky
#

idk how im gonna explain this in a video when i only understand like 60-70% of it

scenic sky
twin portal
#

the script has no calls to RequestSerialization(), so nothing is being synced yet

#

which is a little tricker because we need the owner to make this call

scenic sky
twin portal
#

ah I didn't catch them

#

though, you only need to sync if the adminList has been edited

scenic sky
#

hmm i see

#

here is overview of what this script should be doing

When you press the player or staff button arrows or button themselves, it should only update for you locally.
when you remove a player or add a player we want to send those changes across the network and have the players apply the changes on their end, we need to make sure that they also update their UI so that they aren't trying to add/remove someone incorrectly

our current issues are, the Ui does not update at all
and remote players can't seem to add anyone to staff, i can imagine they cant remove anyone either

#

when i start a build and test i see this though

#

maybe the script is crashing?

lone zealot
#

This is a very common issue. JSON does not have type hints and it also does not differentiate between numeric types. The JS in JSON stands for JavaScript and in JavaScript there only is a "number" type. So when serializing any number (int, float, double) and then consequently deserializing it again, the C# JSON deserializer (by default) has no idea what type it should use for "numbers" so it uses the broadest one: Double.
So when you serialize/deserialize an int you have to access it as (int)token.Double

#

VRChat devs could have just implemented it differently, for example by trying to cast it to an int automatically when you access it as an Int.

But they didnt.

scenic sky
#

That makes sense so I just need to change it to (int)token.Double

#

Thank you for help guys its always a pleasure to have help from this community, i wish I wasn't working today otherwise I'd dig deeper into this but I'm already out of time

#

But thank you for being patient helping me through each problem

#

I'm doing my best to solve as many as I can on my own too

#

This system quickly became a lot more complex than I expect but I'm learning from this which is good

#

I want to really document and comment this out well when it's fully working so I can use it as a good reference and share it with others who want to accomplish similar things

brisk magnet
fallow mountain
#

nonono don't remove cheating, this is realism lol

somber halo
#

Hi, quick question - I'm a bit of a beginner, but I'm trying to send a custom network event with UdonSharp, but I get an error that it doesn't exist on the source object. I want object A to send a custom network event that ends up firing something on object B.

So it's not necessary for it to be on object A since it wouldn't do anything there - is it needed anyway? Can I just do an empty method on A?

frozen igloo
somber halo
#

Object A

#

Object B

frozen igloo
#

You are not currently specifying object B, so it's trying to just call it on the ButtonForTheCube script and that doesn't have that event so it doesn't work.

In order to allow for it to trigger an event on an external script, you would add

public UdonBehaviour otherBehaviour on the ButtonForTheCube script outside of Interact, and then inside of interact you would do otherBehaviour.SendCustomNetworkEvent(NetworkEventTarget.All, etc...

That will make your script capable of sending to an outside behavior, and then in order to link it up to the correct outside behaviour, you would drag and drop it into the slot which should then be visible in the inspector for ButtonForTheCube

somber halo
#

I see. Is there no way to just "broadcast" the network event out for other scripts to receive?

frozen igloo
#

It's entirely possible to build a broadcast event system but that's not what SendCustomNetworkEvent is

somber halo
#

I see. It's more "directed" then? Like sending a specific event to a specific UdonBehavior?

frozen igloo
#

yes

somber halo
#

Alright, I'll modify accordingly. Thank you for the pointers!

rare cloak
#

Hi all, working with VRCPlayerObjects. If I unparent the object during runtime, will it break stuff (the networking aspect)? Trying to make a flashlight system where VR users get a physical flashlight and desktop users get a light attached to their head.
I am unparenting from the instantiated version, not the original

TL;DR does unparenting a child from a vrcplayerobject parent break stuff

twin portal
#

as far as I've seen, no, I didn't encounter any issues when I did that on my on project

fallow mountain
#

(how do you parent to a players head?)

twin portal
#

have it follow the head tracking data

#

can't parent it in a literal sense

twin portal
# twin portal as far as I've seen, no, I didn't encounter any issues when I did that on my on ...

well I did encounter a slight issue but I don't think it was wholly an issue with PlayerObjects; I had about 8 pickups with ObjectSync on them, and because I also had persistence on this PlayerObject, it would persistently save the object's position. So as the object fell with gravity and such, its position was constantly changing and being saved
I attempted to fix this by setting the pickups as kinematic, but incorrectly set this on the rigidbody itself, rather than on the ObjectSync component
At least, that was my theory. I fixed it by just removing the ObjectSync. With it enabled, all players besides the player who owned the PlayerObject would freeze indefinitely, basically crashing the game

fallow mountain
#

so the ones stuck to peoples heads, their object sync should be kept on rather than locally update position every frame?

twin portal
#

to make something follow a remote player you would actually not want to use ObjectSync, because it'll lag behind their position of them that you're currently seeing

#

flashlights as pickups though, would need it

fallow mountain
#

can object sync be individually turned off like this? for the same player object

twin portal
#

I was just looking at that, I would assume disabling the component itself with the enabled property should let you turn it on and off

fallow mountain
#

hmmm... can use this for magic tricks

scenic sky
# scenic sky That makes sense so I just need to change it to (int)token.Double

alright, things are looking better, no more errors and things are mostly networking now, there is a weird issue
so, when either player 1 or 2 adds the names to staff, both players see the names get added to staff (good!) however, this is the bad part. the player who added them sees the names get removed as options from the player list (good!) but the player who didnt add them still sees them as options on the player ui and they are actually able to add those fake players and both player 1 and 2 see the fake players get added and it gets all messy. any ideas? my guess is we aren't properly syncing the fact that the players are no longer available, we are syncing that they got added but dont sync that they are no longer options? im unsure

#

would anyone be willing to look?

scenic sky
#

idk if i can pull this off @twin portal

#

i just seem to keep running into consisnt issues that i dont understand how to fix

#

the script has grown so large i doubt anyone actually wants to read through the mess iv created

twin portal
#

I skimmed through it and didn't see anything obvious

scenic sky
#

theres a lot of issues

twin portal
#

might be time to learn some debugging strats

#

debugging is half the game when it comes to making something semi-complicated

scenic sky
#

everything i tried just seems to not work

#

idk where to begin

#

i see stuff breaking when players leave, ui isnt accurate when i add players to staff for remote players

#

did i just really screw it all up

#

idk if its just the approach iv taken

twin portal
#

usually first thing I test is; are my values actually what I'm expecting them to be, and at the right time?
All the values you want to "see", have it print the info on Update into a TextMeshPro object you place in the world somewhere
Since it's on Update, you know you'll always be looking at the current values of your variables

#

like your list, for example

#

if something's getting out of sync, we want to check if the player actually have the same or different values stored

scenic sky
#

so i should create a ui and paste the results there each time?

twin portal
#

yep. just a TMP wall of text will do

#

set the text to whatever you need to see on Update

scenic sky
#

on update?

#

like are we talking the method

#

or?

twin portal
#

yes

#

the Unity event Update

#

that way it's updated every single frame

#

no worrying about "do I actually have the data or am I just not displaying it in the UI"

scenic sky
#

alright

#

just trying to think of how id display the results of FindPlayers

twin portal
#

just make a temporary string variable, then loop through the array and paste all the names into it, then concatenate that into the final debug text

#

if you wanted to save space you could just output the Length of the array

scenic sky
#

oh

twin portal
#

but might be useful to see the names

scenic sky
#

i was about to do this

twin portal
#

that ideally should be the same as the playerList count, but you're technically not getting that array's length by just getting the player count from the VRCPlayerApi

#

you can do playersList.Length instead

scenic sky
#

alright

twin portal
#

also, there's a trick with strings so you can put variables in them easier, without having to use the + to "add" strings together

#

if you add a $ to the front, you can use the variable name and put brackets around it, and it'll put the variable's value there

#

like this: $"Players List Count: {playersList.Length}"

scenic sky
#

oh yeah i forgot i can do tha

#

hmm im getting an error

#

it aint happy

#

with the line here

twin portal
#

did you assign your TMP object in the Inspector?

scenic sky
#

yup

twin portal
#

what part of that line is column 69

scenic sky
twin portal
#

if you put your cursor anywhere on the line, you should see something on I think the bottom-right corner that tells you what line and column you're on

#

forget how it looks in VS

scenic sky
#

i only see numbers on the side

#

"Players List count: "playersList.Length is the issue

#

removing it removed the issue

#

i wish i had more time. but sigh gtg to work again

#

thanks for the help again

twin portal
#

I can see why that is happening
playersList is not initialized to anything when you first declare it; it doesn't get a value until FindPlayers() runs

#

so just need to add something like private VRCPlayerApi[] playersList = new VRCPlayerApi[0]; to the variable declaration

scenic sky
#

Alright

crude trail
twin portal
#

why do it in start when you can just set it at the declaration?

crude trail
#

Donesnt work for everything.

#

Mostly just localplayer but i do a lot of setup in my scripts that happen on init

twin portal
#

right... in this case it will work before Start

#

assigning the local player though, has to be done in Start, since the player does not exist prior to compilation of the script

scenic sky
#

yeah i think im at the giving up point. no matter what i do, the player list is always gonna be the problem. The player list UI doesn't update for remote players when someone else adds staff ,and its not even just the UI thats bugged, its the list itself because that remote player can add the same names to staff despite the fact that they are already on it.
somehow at the start i got my 2 clients, same name but as soon as i move them both to staff, the other remote player can add them again as if there is 4 clients.

I think this is just way out of my skill range at this point. unless someone knows what im doing wrong

warped latch
#

Trying to confirm something that just absolutely drove me nuts: GetPlayerID returns an id associated with a given player in an instance, but those ID's aren't consistant across the network?

fallow mountain
#

it is consistent inside the instance across all the players, the first person joined is 1, second person joined is 2, third person joined is 3, if second player leaves and re-join they become 4, so on

warped latch
#

I'm not finding that as the case

fallow mountain
#

?

warped latch
#

It's probably just network shenanigans, I'm having issues keeping IDs straight

twin portal
#

the ID will be the same for all players, within that instance

scenic sky
#

@twin portal should i just give up on it.

twin portal
#

never give up. never surrender

scenic sky
#

im at a loss

#

ai. useless. me, not skilled enough

#

im terrible at explaining so i doubt anyone can help

#

assuming anyone wants to at this point

#

its just such a big script

#

i thought maybe just once i'd actually be able to create a useful system that would not only help bit help others

#

because there is 0 tutorials out there or assets that let you do this

#

the only one iv seen is $30

#

which, you know great for them they worked hard to make it

#

but it would hurt me inside to buy something i can learn to make

#

and id much rather learn to make it myself so i can show other people

#

the best way i can think of describing my issue is like this
The player list UI doesn't update for remote players when someone else adds staff, and its not even just the UI thats bugged, its the list itself because that remote player can add the same names to staff despite the fact that they are already on it.
somehow at the start i got my 2 clients, same name but as soon as i move them both to staff, the other remote player can add them again as if there is 4 clients.

for example when i start up a build and test i got 2 Koko's on the player list UI
any player can add them to staff as instead
when a player adds them, both players see the names get added to staff but only the player who added them sees the names taken away from the player list ui as options. its supposed to do this for all players. but since it doesnt do that, that means the other play can somehow add those 2 names to the staff list then there is magically 4 koko's despite the fact that the script has parts in it that check to see if that ID is already staff or not.

frozen igloo
scenic sky
scenic sky
#

because iv seen things just outright get ignored in it

frozen igloo
#

yeah, the root of it might be somewhere else but at the very least, that delay will make it difficult to be certain what's happening with sync

scenic sky
#

but i could try

frozen igloo
scenic sky
#

it was in a past project, i remember doing some checks in there and it just never worked

#

it was awhile agp

#

so i just assumed i should put a method instead

scenic sky
twin portal
#

I think the AI suggestions may be hurting you more than they are helping

scenic sky
#

yup

#

thats why

#

i hate

#

using it

frozen igloo
#

yeah, if the goal of RefreshUI is to take the current state of StaffList and PlayerList and display them on a board, then you want to do that immediately in ondeserialization

scenic sky
#

imma pull up my older version of the script before the ai suggestions

frozen igloo
#

if FindPlayers is a dependency of that, then yes it needs to be ran in ondeserialization too. Or it could be put into a separate Initialize() function and called once if needed anywhere

scenic sky
scenic sky
#

@frozen igloo this is what it looks like when i add a player

#

but for the other player it looks like this

#

once both are added it looks like this for player 2

#

you see (empty!)

#

but

#

i can click the empty button and still add the 2 names over anyways

#

now i magically have 4

#

this is with the changes

frozen igloo
#

mmk, I'm looking at the selection code to see what you're doing there

scenic sky
#

alright

frozen igloo
#

so is it intended that the UI is also synced, so everyone can see exactly what the person operating the panel sees?

scenic sky
#

so, the buttons and arrows are meant to be local, but they should only be effected by the network is a player got added or removed

#

for example lets say player 1 had player 1 on their button ready to select. same with player 2. this is local. when player 1 adds player 1 to the list, we need to shift the index for all players so that player 2 can't see player 1 as an option anymore

#

idk if this would be easier to just sync the index or not

#

i thought it wasnt needed and would be nicer to the network to do it locally

frozen igloo
#

It seems like this revolving selector UI is making things a lot more complicated than it needs to be

#

It would be easier to navigate and more straightforward to build if it was a vertical list of players with buttons next to them which performed actions like setting to staff

scenic sky
#

how would you do that?

frozen igloo
#

there's a handful of ways to build a UI like that. Some techniques are manual and involve a fixed list of UI buttons which get enabled/disabled as necessary. Some scripts can be used to automate the generation of that. Or the runtime scripts could be built to be capable of automatically instantiating/destroying objects to fit

scenic sky
#

sounds complicated 😅

frozen igloo
#

not as complicated as dealing with off by one errors and navigating up and down a list that can change out from under you at any moment 😅

scenic sky
#

i mean fair but i got no idea how to do that, let alone where i can learn it

frozen igloo
#

right here ^^

scenic sky
#

its just such a garbage feeling when i cant figure out stuff for myself and have to rely on ai, or asking people constantly

#

it just makes feel like im not independant at all

#

idk if that makes sense

#

like i have to have my hand held to doanything

frozen igloo
#

This stuff is complicated, nobody knows how to do any of this just naturally

scenic sky
#

it honestly feels like everyone knows

#

but me

#

and i dont know what i have to do to get to a point where i can be like you guys

#

iv been doing this on and off since 2019, whats my excuse for not being at that point yet

twin portal
#

I've been doing this since I was a child, and went to school for it 😅

scenic sky
#

i apologize for the negative attitude, im just feeling a bit overwhelmed and frustrated with my own incompetency

twin portal
#

it's very understandable to need to ask people on how this stuff works

scenic sky
#

im applying for a game design college very soon

#

because i think i need to learn this stuff properly

#

but idk how much of that is actually gonna trasnfer

#

like the networking is just a nightmare to learn

#

i practiced with normal stuff and it did help but as soon as i reach anything networking wise, i just get slammed and stuck

frozen igloo
#

Once people already know stuff, most of them don't hang out in question-answer forums. This isn't where the communities are, this is just a launchpad. But a handful of people that you see talking in this channel like hanging out here answering questions. We do that so that people like you have the resources you need to learn and grow to the point of being able to launch yourself.

This channel's entire purpose is for questions like that. That's an excellent beginner project that will teach you some key concepts. I'd be happy to dig into that if you're willing.

twin portal
#

spot on. I'm mainly hanging out here so often because I see so many people get frustrated when it comes to learning how to code, and I do my best to help.

frozen igloo
#

also once you do know stuff, teaching it can be a good way to reinforce the knowledge. It's fun, all around

foggy jackal
#

Yep, all of that, and maybe, just maybe we can help people make better content 🙂

scenic sky
#

i just dont wanna post

#

bad or inaccurate content

scenic sky
foggy jackal
#

Also a willingness to learn goes a LONG way towards others wanting to actually help you 🙂

scenic sky
#

because i feel like there is a lot of ways i, and other can benefit from having a system like this around with a tutorial and public source code

scenic sky
#

this is my only place i got so far

frozen igloo
#

ok let me start a thread

twilit pewter
#

Just double checking myself. When using the OnDeserialized event, do we need to include a base.OnDeserialized() inside it to avoid breaking it as an override?

        public override void OnDeserialization()
        {
            base.OnDeserialization();
            //My code would go here.
        }```
honest lance
#

no, base.OnDeserialization does nothing, if you are extending UdonSharpBehaviour directly

twilit pewter
#

Good to hear! That's what I thought, but figured it didn't hurt to ask. Thank you for giving me confidence!

#

Been doing a lot of editor coding lately, so I started to doubt myself a bit with UdonSharp.

twilit pewter
#

Is there a method I can use to pull all PlayerObjects for all players and get one specific script from all of them?
Guessing I might have to make use of Start() on the PlayerObject scripts and call a function on an isolated script that creates a new array and adds the PlayerObject script to it.

honest lance
#

You can iterate through an array of all VRCPlayer's and use FindComponentInPlayerObjects. (And cache the result, updating the array as needed with OnPlayerJoined and OnPLayerLeft)

twilit pewter
#

Thinking your suggestion is probably better than what I was thinking. Definitely fewer lines of code to run.

twilit pewter
#

Given the mention of avoiding Destroy on objects with Networked UdonBehaviours here:
https://vrc-beta-docs.netlify.app/worlds/udon/networking/events/#legacy-events-and-security

There is also no mention of Player Objects here regarding Networked Variables:
https://vrc-beta-docs.netlify.app/worlds/udon/networking/variables

There is also no mention of using Networked UdonBehaviours or Networked Variables, but mention of object ownership as if it could apply:
https://vrc-beta-docs.netlify.app/worlds/udon/persistence/player-object/#ownership

Is it problematic to have Player Objects with Networked UdonBehaviours or Networked Variables? This could be a great security feature and might be intended as such, but since it's not documented as being supported or recommended, I'm asking here.

heavy spindle
twilit pewter
# heavy spindle Not problematic. PlayerObjects have networking support

My own tests confirm this as well, but there's no documentation on it that I can find that it's actually supported or has any specific limits to be aware of.

I've had folks bring up limits of Unity's Photon Engine, since we cannot be aware of everything VRChat needs internally, that makes the limits of Networking with Player Objects very ambiguous.

There are worlds that have hit 300 members, which code I've written has been in without my intervention, would that make just four variables on a Player Object catastrophic or not? It's very hard to be sure if even one Synced Variable on a Player Object is something to avoid as a general practice since other scripts using them that anyone doesn't know about could be very problematic with this ambiguity.

heavy spindle
twilit pewter
#

Guess for now I'll YOLO the code since it's necessary for a current project. If it doesn't break then great ^.^

heavy spindle
#

I'm personally uninitiated with any limitations of Photon or how VRC handles its networking backend. Would likely be something for Phasedragon to answer if and when they see this. Honestly just fuck it we ball

#

Though if you're hitting 300 members in a single instance, I think you have bigger problems to worry about such as CPU exhaustion/other performance problems

twilit pewter
twin portal
twilit pewter
#

Also would love to know of any limits if folks have found any. That knowledge shapes best practices.

finite sierra
twilit pewter
twin portal
finite sierra
#

eh? thats a first considering they never showed that at Furality.

finite sierra
#

oh yea that one. had forgotten that existed.

twilit pewter
finite sierra
#

around 80 players Vrc typically struggles. with synced variables.

twin portal
finite sierra
#

like in my own creation we had a hard limit with 80 people that each had 4 synced variables. and only around 24 bytes per person. + another 16 so 40 per person and around 80 players it started to hit data limits.

#

these 4 synced variables also only changed when a player requested it. and that was limited to once per 30 seconds.

twilit pewter
finite sierra
#

@twilit pewter thats what i have been telling you 😄

#

4x80 synced variables. and a semi regular SendCustomNetwork every 30 seconds would cause a fair bit of problems.

twilit pewter
finite sierra
#

but it all depends on so many dam things.

#

like how fast it changes.

#

how many players etc

twilit pewter
finite sierra
#

cause it does seem that each player adds another extra layer of complication to how much we can even nuse.

twin portal
#

yeah bandwidth is the main limit

#

which is detailed in that first page

finite sierra
#

and with my own testing and checking it does seem like each player joining the world adds to the overall data output.

twilit pewter
finite sierra
#

on average with 60 ish people it would often hit 7-11 kb/s per person

twilit pewter
#

Thing is, if Photon has limits regarding how many Networked Properties there can be, if that applies to VRChat, that'd be good to have in documentation too.

finite sierra
#

even if 60x40 is only 2400 bytes

twin portal
#

for example, the size of all parameters in a single networked event can't exceed 16KB

twilit pewter
twilit pewter
finite sierra
#

realisticly you cant exceed 8-10 kb/s at max no matter what

twilit pewter
#

A string with JSON data would circumvent any limit on how many synced variables we can use. Issue comes with awareness of other packages that assume the same thing and may not be up front about using Player Objects with Synced Variables. The number of Synced Variables in total could be problematic if it is a factor, not in terms of bandwidth, but in terms of allocation.

For example: If you bit packed a byte, you'd be limited to 8 bits, that'd be 8 variables essentially. If you limited bandwidth to 1 bit per second it wouldn't matter overall if you wanted to try using a 9th bit.

finite sierra
#

well no. the Json would at some point be to big. you would still be limited to the max of 8-11 kb/s no matter what. a Json even has some overhead just to be created etc.

twilit pewter
# finite sierra well no. the Json would at some point be to big. you would still be limited to t...

That's very much a factor with using JSON, which I absolutely agree isn't ideal. Say I had 4 booleans per player, which could be 4 separate systems using Player Objects. Even if the overall impact is as low as it could be per system, there's a lot of potential for bloat even on the smallest possible footprint.

It's easy to see that this could be a factor that makes it not recommended or a best practice to utilize. That said, it depends entirely on aspects of VRChat's internal handling that we can't know about unless we either hit those limits or there is documentation for it.

#

Chances are high that in the most extreme use case of a high capacity instance, what is best practice will be extremely good to know.

finite sierra
#

well tbh vrc isn't ment to handle alot of throughput. so we are very limited to what can be done

twilit pewter
twilit pewter
hybrid kindle
#

how would someone make a local timer?

strange token
#

Probably just send custom event delayed seconds for one second, then remove 1 each time it runs and display the value

twin portal
#

normal custom events are not

#

SendCustomEvent vs. SendCustomNetworkEvent

strange token
#

I never work with network events

#

All my networking is just field change callbacks (property callbacks) on variables

hybrid kindle
#

where tf is it going global then 😭

#

this is local correct??

twin portal
#

well...

#

technically

hybrid kindle
twin portal
#

but player positions are synced

#

and OnPlayerTriggerEnter fires for ANY player that is seen colliding. Including remote players

#

OH

#

that IsValid should be a branch

twilit pewter
hybrid kindle
twilit pewter
#

I'd recommend you use SendCustomEventDelayedSeconds

hybrid kindle
#

thanks guys

fallow mountain
#

Depending on purpose, I would use Time.Time or Time.unscaledTime or even Time.realtimeSinceStartup
Time.Time for game logic
Time.unscaledTime for clocks and UI
Time.realtimeSinceStartup is good for benchmarks

strange token
#

Tfw there’s more helpers than askers on a Tuesday morning

twilit pewter
# hybrid kindle code i got it

Something I've learned regarding SendCustomEventDelayedSeconds is you cannot cancel the timed event once started. You can fake it though if you may want to.

Say you only want to have one event of the timer active at a time:

  • Have a boolean default to true.
  • Check if that boolean is true then:
    • Set that boolean to false before SendCustomEventDelayedSeconds.
  • Set the boolean to true in your target event.

Say you only want the last time the timed event is fired to actually run:

  • Use an integer that you add one to right before SendCustomEventDelayedSeconds.
  • Subtract one from that integer immediately in your target event.
  • Immediately after that subtraction, check if that integer is zero before continuing to run your intended code.

If you want to be able to "cancel" the last event (imagine you cut power to a device that was charging up to fire something):

  • Set a boolean to true immediately before SendCustomEventDelayedSeconds. (Could also be before an interger you're adding to, if you're also doing that.)
  • In your target event, check if the boolean is still true before continuing to run your intended code. (Could also be combined with checking an integer if it is zero, if you're also doing that.
  • Set that boolean to false wherever you may want to cancel that event for.
hybrid kindle
twilit pewter
finite sierra
twilit pewter
#

I got to head to work, but I'd absolutely add that into my post if I had more time >.< Had to warm up the car, so added it in 🙂

twilit pewter
strange token
#

I like your previous solution, do you have one in mind for that issue? >.>

#

My only thought has been somehow counting how long it’s been since the method was called and if the time value you’re comparing against updates it measures a smaller time interval than expected which means you called it again so the old one dies, but it’s just a theory with no real code put behind it

fallow mountain
#

Yeah maybe save the Time.time as a float variable e.g. timeFired, and in the delayed event, check if time.time is greater or equal to timeFired by the delayed time, and only continue if true. This ensures only the latest firing will run

finite sierra
fallow mountain
finite sierra
cold laurel
fallow mountain
#

what exactly is the method that needs to be one frame late and maybe cancelled?

finite sierra
#

you cant cancel it ever. once u send it it has to execute. the only time you can ever stop it from doing it if its a self repeating one. like SendCustomEventDelayedFrames that is in a repeating method. like for instance instead of using the Update method do something like ``public void _OnUpdate ()
{
// then add a check here like a boolean to stop it from executing again.
SendCustomEventDelayedFrames(nameof(_OnUpdate),1)
}`

fallow mountain
#

maybe u can cancel like this

[DefaultExecutionOrder(0)]
public class First : UdonSharpBehaviour 
{ 
    public bool canFire = true;
    void CustomEvent()
    {
        canFire = false;
    }
}

[DefaultExecutionOrder(1)]
public class Second : UdonSharpBehaviour 
{ 
// if canFire true
    // do stuff
}

finite sierra
#

That's not gonna do anything. The execution order doesn't mean it will execute right after each other on top of that it would mess with any other scripts executing as you tell unity that no matter what those two always have to execute before anything else

fallow mountain
#

why wouldnt it execute in order? and why would it mess with other scripts?
all the scripts by default runs in 0

twin portal
#

it says the order can be negative so maybe the first could be -1 to force it to run before any other behaviours?

finite sierra
#

Generally it's not recommended to mess with the order unless you know what you are doinh

fallow mountain
#

...?

finite sierra
#

Even unity them self tells you this

fallow mountain
#

so if iKnowWhatImDoing is true, then it works

cedar flume
#

hm... wanting to do networking coding in U# to send variables about related to my script...
How should I do so? Is there a video of doing in that manner (not graph)?

finite sierra
fallow mountain
#

the traditional way to sync variables is to request serialization, there is a good video from before:
#udon-networking message

cedar flume
twin portal
#

this is all for Manual sync btw

#

a good analogy for Manual sync is like mailing a letter:
RequestSerialization is "I've got a letter ready to send. Please send it at the next available chance."
OnPreSerialization and OnPostSerialization are optional for your script to work with, but can be very helpful.
OnPreSerialization fires right before the serialization is about to be sent. It's basically "Hey I'm about to mail out this letter. Is there any last-second info you want to put into it?"
OnPostSerialization fires right after the serialization is sent. It's like the mailman coming back to you with "Hey, I tried to send your letter. It was successful and it took up X amount of space in my mail van." or "sorry, sending your letter failed."

twin portal
# cedar flume hm... wanting to do networking coding in U# to send variables about related to m...

you might also like the new NetworkEvents with parameters that are currently in beta, they're pretty neat (and in a lot of use-cases, can save you the effort of needing to juggle all this serialization mumbo-jumbo)
https://vrc-beta-docs.netlify.app/worlds/udon/networking/events/

Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.

fallow mountain
#

I think the key difference is, serialization is for syncing variables, that is having variables that are persistent and consistent across the instance (and ready for late joiners). Like an HP bar.
Whereas the new events with parameters, is still an event, and is meant to be "sent and forget", and is not meant to persist or remembered (or available for late joiners). And especially suitable for non-owners to tell the owner of a variable something has happened and should be changed, without changing the variable directly (e.g. variables that cannot be changed owners like an HP bar stored on a Player Object). So the event can say "damage by 20 HP" for example.
So I suppose it depends what you are sending and for what purpose. The two methods are for different use cases.

twin portal
#

definitely, all depends on what you want to do

#

though I did devise a little system that.... I think actually worked faster than continuous sync, with the new events

#

basically just sent the data to everyone, but whenever the owner received the data, they would save it to a synced variable

fallow mountain
#

interesting, any risk of more desync? cus i had the impression that events are less reliable but not sure

#

i may be wrong, maybe events dont suffer more "packet loss" than serializations (so equally reliable)

twin portal
#

I wanted to get a bigger sample size but I had about 5-6 people interacting with the (pretty simple) test to an alright degree

#

even if there was a desync, eventually the owner would sync their saved value with everyone so things would recover quite quickly

#

I imagined the system like how something would work with an FPS game, where everyone is shooting the same enemy very quickly and tried to design around that
So multiple people can all affect the same "health" of the enemy, but it would fluctuate a little. I think if the synced value was just in a healthbar without a visible number, it wouldn't be noticeable

cedar flume
twin portal
#

VRChat's networking works really well for that kind of stuff, should be pretty doable

cold laurel
north thistle
#

What kind of desync are you worried about that would discourage using networked events? I've found using networked events (especially the new ones with parameters) to be incredibly powerful, personally.

#

You don't have the issue with continuous sync where it is always running and consuming your networking bandwidth and you avoid the risk of intermediate values being lost like with manual sync. Also events with parameters allows you to transmit data with more contextual meaning, reducing complexity and the amount of extra information you need to accompany with it

#

Also, does anyone know of a simple way to determine when the real time that an event was sent by a remote player?

I was thinking of having a single object whose only purpose is to manual sync once at instance creation and then when people load into the instance they read the sendTime in DeserializationResult to determine determine what their Time.realtimeSinceStartup is in relation to the instance time. And include the instance time the event was sent at as one of the parameters.

But maybe there is a simpler way of doing that?

#

Alternatively would Networking.SimulationTime(NetworkCalling.CallingPlayer) gives me a reliable idea of when the event was sent?

frozen igloo
frozen igloo
frozen igloo
north thistle
#

I'm just not sure what my options are for sending a "current time" that is usable by remote clients. For example I assume Time.realtimeSinceStartup would not work. I think my manual sync idea would work, but I always like having more options.

frozen igloo
#

I think DateTime.Now.ToBinary() and DateTime.FromBinary would work, but test it to be sure

north thistle
#

I'll look into it, thanks!

frozen igloo
north thistle
#

Ahh it even takes into account different time zones; nice

frozen igloo
#

Networking.GetServerTimeInSeconds() and Networking.GetServerTimeInMilliseconds() are also a thing

#

but you may have to deal with wraparound and/or precision loss with those

north thistle
#

Is the server time unique to the instance or is it more global?

frozen igloo
#

server time is a measure of how long the associated photon server has been open. Servers manage multiple instances and stay up for months, so these numbers may overflow and wrap around, sometimes being negative

north thistle
#

I was worried that is what you meant by wraparound issues, lol

#

Thinking about it, as long as you accept that the instance being open for a significant amount of time will cause issues, it shouldn't be too hard to work around the wraparound. But at that point it probably isn't anymore elegant than the manual sync method.

#

(and if my math is correct, a "significant amount of time" is over 24 days)

north thistle
#

I think this should work, right?

//returns true if the first network time is sooner than the second, taking into account wrapping
public static bool IsNetworkTimeSooner(uint firstTimeInMilliseconds, uint secondTimeInMilliseconds)
{
    //check if a wraparound has occured
    if (Math.Max(firstTimeInMilliseconds, secondTimeInMilliseconds) - Math.Min(firstTimeInMilliseconds, secondTimeInMilliseconds) > 2147483648)
    {
        return firstTimeInMilliseconds > secondTimeInMilliseconds;
    }
    else
    {
        return firstTimeInMilliseconds < secondTimeInMilliseconds;
    }
}
fallow mountain
#

The SDK video players use server time to sync their playback, but it is a simple calculation of local time offset from server time, there is no correction for wrapping or anything. I wonder if you really need it, do/can instances even stay longer than a day?

#

And is server time the only way of time measurement that is consistent across players (since their local clocks can be wrong)?
I mean how accurate is it, is it automatically accounting for network latency? Or is there any extra calculation needed for very precise syncs?

cold laurel
north thistle
cold laurel
#

No?

north thistle
#

Why not?

cold laurel
north thistle
#

So? I am already expecting that behavior

cold laurel
north thistle
#

I'm assuming that Networking.GetServerTime that is running on the server is being treated as a uint that keeps counting up until it hits uint.MaxValues at which is flips over to 0; that is why when the instance connects to a server that has been running for awhile and you read it as a int you get a negative value and get confused

twin portal
#

I think that is an incorrect assumption

#

it overflows because it's being treated as a signed int, no?

cold laurel
#

It would overflow regardless of what type of integer it gets treated as.

twin portal
#

is overflows to negative because it's signed, that is

foggy jackal
#

I'm a little curious why that would be signed

cold laurel
#

Well, yeah, but the problem isn't that it becomes negative. The problem is that it becomes smaller.

twin portal
#

sounds like a proper headache to account for

north thistle
#

Actually it's irrelevant if the bytes are being treated as signed or not as int.Max [2147483647] + 1 is the same value as uint(2147483647 + 1)

#

So just read the server time as an unsigned value and you'll be good

twin portal
#

but it would still overflow?

#

reading it unsigned doesn't fix that problem

north thistle
cold laurel
#

It becoming negative is not the issue. It being unsigned or not doesn't matter.

#

You've fixed nothing.

north thistle
#

What issues remain with my approach?

cold laurel
#

If the first timestamp is before the overflow happens, and the second timestamp is after. Then the second timestamp will be smaller than the first timestamp.

finite sierra
north thistle
cold laurel
north thistle
#

I typed 2147483647

#

Oh you mean in my code? I might be off by one, I wasn't super caring though as it doesn't really matter for what I'm doing

#

2147483647ms is almost 28 days; a single millisecond difference isn't going to matter at that point; and I'm not expecting to need to compare two timestamps from the same instance that are that far apart anyways

cold laurel
#

You have a fundamental misunderstanding of the problem.

north thistle
#

In what instance would my code fail?

#

Can I not treat Networking.GetServerTimeInMilliseconds as a 32 bit array that increments by 1 bit every millisecond?

#

hell even if on server side it is a 64 bit array and we are only sent the lower half, it's effectively the same, right?

cold laurel
#

Array?

twin portal
#

an array of bits I guess is kind of a way to imagine how an int is stored

#

not quite the same but I see the idea

north thistle
#

I didn't know the correct technical words to describe it and array does get the idea across, yah

twin portal
#

so what your code does is flip the comparison of the two server times depending on if one is larger than the other

#

I think the question of "does this work or not" comes down to if this statement is the correct way to do it
if (Math.Max(firstTimeInMilliseconds, secondTimeInMilliseconds) - Math.Min(firstTimeInMilliseconds, secondTimeInMilliseconds) > 2147483648)

north thistle
#

since a value close to uint.MaxValue minus a value close to zero is going to be a massive value, if I do a subtraction between the larger of the two values and the smaller of the two values and get a massive number, then a rollover is presumed to have occurred

twin portal
#

I see it now

#

how this would work in a practical example I'm not fully aware of, though

#

I feel like there are easier "timestamp" values to use than this one

north thistle
#

Phasedragon's suggestion to use DateTime.Now.ToBinary() and DateTime.FromBinary would probably be the easiest, but would require you to either trust or not care if players change the local time on their computers

minor gale
#

CalculateServerDeltaTime(double timeInSeconds, double previousTimeInSeconds) is wrap safe if you're working with the seconds version of the server times

#

the ms version is in int, but i don't think it hits negative numbers so if you cast them to a uint and compared they would wrap safely i think? (or even if they do hit negatives, casting to uint after subtraction is probably the correct thing to compare)

#

per photon

It can start with any positive value.
It will "wrap around" from 4294967.295 to 0!
north thistle
#

Hmm, if that is the highest value then precision loss isn't going to be meaningful, right?

minor gale
#

yeah precision loss in terms of the value precision isn't going to cause issues i imagine, precision in like accuracy terms is questionable because of how they poll the server time initially, but it's in the range of like +- 20ms and consistent

north thistle
#

Thank you, thank you. I only have a vague understand of how precision loss works with doubles/floats so when it comes to that stuff I can only guess.

minor gale
#

if someone had the game open for more than ~18 hours they would start to get precision loss when using stuff like SendTime (which is provided when you receive synced data) because vrc keeps them relative to a float (Time.RealtimeSinceStartup)

#

but the server times themselves shouldn't be in float territory if you used them for timestamping

north thistle
#

Thanks for the info!

minor gale
#

for posterity, this is probably how timestamp deltas should be checked when using the ms version of servertime

north thistle
#

Also testing out some things, I think my code would crash in practice. As far as I can tell, C# will refuse to convert a negative int to a uint unless you use the unchecked statement; and if you've ever used U# you know where this is going... UdonSharp does not currently support node type 'UncheckedExpression'

minor gale
fallow mountain
#

there is also Time.timeAsDouble or Time.realtimeSinceStartupAsDouble etc
how much more precise are they idk

minor gale
#

the issue is that vrc provides the sendtime in single precision relative to the latter

north thistle
#

Also I'm specifically looking for a timestamp that can be sent over the network from one client to another and be understood by the receiving client

minor gale
#

also i hadn't read what you were originally trying to do, i think if you want to figure out the datetime for someone doing an event, you should send the GetServerTimeInSeconds() as a timestamp

get the delta with delta = CalculateServerDeltaTime(GetServerTimeInSeconds(), syncedTimestamp)

so you have the seconds difference, you can make a deltaTimespan = TimeSpan.FromSeconds(delta)

DateTime.Now - deltaTimespan is when it happened

north thistle
#

Yah to be more specific with what I am trying to do:

there is an item on the ground that can be added to an inventory by interacting with it; I want it to be seen by and interactable with by all players but I want only one player to be able to add it to their inventory

The idea is that interreacting with the item will send out a "I claim this item at this time" networked event and then immediately (or after a very short delay) the item is added to their inventory. Then if the person receives a "I claim this item at this time" for the same item from another player, it compares the time they claimed the item vs the remote user's claim. If the remote user claimed it first then any necessary rollbacks occur.

minor gale
#

personally i think you should arbitrate that through the master or some central figure who can respond telling them they can claim it, to prevent race conditions like that

fallow mountain
#

yeah i wouldnt do rollbacks, just having the owner decide (first come first served) who got it and response with an event, no need to mess with rollbacks and timestamps, let the lag be lag

minor gale
#

you could also override the OnOwnershipRequest method, which would tangibly lock out people who come after (the owner rejecting their request); the "losers" would receive an OnOwnershipTransferred after the fact, where they could infer that they lost ownership of it despite clicking on it

doesn't feel as good to me though

#

for what you want to do with times, if you prefer sticking to timestamped events, you could just use their local utc.now, even if it's not technically correct which came first because of clock differences, both clients would agree that some tick time is before or after another

(DateTime.Ticks is probably what i'd send in that case)

fallow mountain
#

wouldnt server time be more appropriate?

minor gale
#

probably yeah, i don't really like server datetime because i've observed negative trip times from it, but it should be more aligned than pc clocks

fallow mountain
minor gale
#

oh the servertime? yeah you could directly compare the delta i guess with the method they provide, newer incoming stamps would be > 0

#

that's probably a lot easier

#

the logic for newer would become

OnPickup()
myPickupTime = Networking.GetServerTimeInSeconds()

OnEvent()
myTimeIsNewer = Networking.CalculateServerDeltaTime(myPickupTime, incomingTimestamp) > 0d;

if myTimeIsNewer, drop item
#

maybe that's not the thing you'd actually want to check, i guess they'd both be timestamps on the object

north thistle
#

mmhm

fallow mountain
#

interestingly, double should be plenty good enough for centuries, says gpt

| Elapsed Time         | Seconds        | `Time.time` (float) Precision   | `double` Precision        |
| -------------------- | -------------- | ------------------------------- | ------------------------- |
| **1 hour**           | 3,600          | 0.429 ms                        | 0.0000000008 ms (0.8 ns)  |
| **4.66 hours**       | 16,777         | 2.00 ms                         | 0.0000000037 ms (3.7 ns)  |
| **1 day**            | 86,400         | 10.3 ms                         | 0.0000000192 ms (19.2 ns) |
| **3 days**           | 259,200        | 30.9 ms                         | 0.0000000576 ms (57.6 ns) |
| **11.6 days**        | 1,000,000      | 119.2 ms                        | 0.000000222 ms (222 ns)   |
| **1 year**           | 31,536,000     | 3,759.4 ms (3.76 s)             | 0.000007 ms (7 µs)        |
| **31.7 years**       | 1,000,000,000  | 119,209 ms (119.2 s)            | 0.000222 ms (222 ns)      |
| **317 years**        | 10,000,000,000 | 1,192,093 ms (1,192 s)          | 0.00222 ms (2.22 µs)      |
| **3.17 million yrs** | 1e15           | 1.19 × 10¹¹ ms (1.19 million s) | 222 ms                    |
| **317 million yrs**  | 1e16           | 1.19 × 10¹² ms (11.9 million s) | 2,220 ms (2.22 s)         |

north thistle
#

And having the master resolve things, I was going to show reservation about allocating an extra task to the master in an already network constrained world (the main goal I'm attempting with this world is seeing how many networked agents I can get running within a single instance), but I'm already distributing the most network heavy tasks and the master appears to get extra bandwidth on top of that, so it would probably be fine

minor gale
#

nav agents?

north thistle
#

yep, each with an attached AI script

minor gale
#

you can run a somewhat arbitrary amount if you only sync the destinations, but that can obviously desync more heavily than periodically syncing their position

north thistle
#

mmhm; I have considered it a lot but always felt the concessions I'd need to make to avoid desync would be too great

minor gale
#

i think if i was trying to run a large amount with periodic sync i'd use playerobjects and have each player control x amount, which scales pretty naturally with playerobjects

#

but i guess it depends if you want them to be able to change ownership

north thistle
#

Best compromise I've found right now is to have a VRCObjectSync attached to my nav agent and then in the child put my AI script that sends out networked events as needed

Interestingly having VRCObjectSync and my script on the same object severely reduces network performance (my best guess is that it interferes with VRCObjectSync's ability to prioritize itself)

#

I'd use playerobjects to distribute them, but doing so would prevent ownership transfer and there are some circumstances where I want to transfer ownership

Instead I have the host distribute the agents as they are spawned in, with each player having a soft and hard cap of how many agents they can be assigned

minor gale
#

i think you might benefit from using manual or events over vrc's object sync, like rotation can be inferred from travel direction (or as a single float for the yaw)

but it kinda depends and object sync is probably more convenient

north thistle
#

I tried using events but was running into the max 100 events per second limit. Looking back, however, I might have messed something up with the implementation as I was distracted talking with someone when I implemented and then immediately reverted it.

With that said I am currently sitting at a max of 35 agents per player which seems pretty usable

minor gale
#

for events i think you'd want to use a central manager to avoid the header per event, but it introduces more complexity than just sending them at object level

something like push new state to manager (locally, don't allow duplicates and only takes newest state); package all of those queued agent states to bytes every interval (sync rate)

#

ownership/state could also become a bit nebulous (people could push states to things they no longer own), but it sounds like they wouldn't be changing hands super often

north thistle
#

I'll certainly keep that in mind for future networking optimizations. Right now the biggest networking contain is the about of networking capacity used by movement sync. The AI script itself only uses events and sends them out relatively frequently. Although in the future I'll likely change it into a queued system for damage events to prevent event spam.

fallow mountain
#

if ai follows predictable path, you just need one event at the start to launch them

north thistle
#

they do not, unfortunately

minor gale
#

i more meant events for the movement, as object sync probably has overhead compared to doing your own movement sync (via manual or events, though manual should beat out both); doing damage events at object level sounds fine unless they happen really often (you could just pool them on the object at that point prior to sending the cumulative damage that occurred over some timeframe)

north thistle
#

That might work, yah. Although over time I've suspected that vrcobjectsync has a privileged way of interacting with the networking limits... guess I'll find out if that's true

minor gale
#

i do think manual scripts are limited to 40 objects per second, so object sync using continuous could interact a bit differently if you have a lot of objects

north thistle
#

by manual scripts do you mean manual sync?

minor gale
#

yeah

north thistle
#

so to make sure I understand you correctly, if I am using manual sync to sync location data, I'll either be effectively limited to 40 active nav mesh agents or I'll have to group the location data together and sync them collectively?

frozen igloo
#

having many independent objects will waste a lot of space on headers, so yeah you'll be able to get more by packing them together

north thistle
#

What about the "Group" that each network object is put in? ("Grouping in this context is an internal networking system used to combine multiple objects together by distance so that their data can be sent together.") Would this interfere with that? Would it even matter?

#

I'm not sure if that "by distance" is arbitrary or serves some specific goal

minor gale
#

if it's like other networking, that sounds more like it'd be related to users receiving the data (if an object is especially far away it might not prioritize sending them that data compared to an object directly next to them)

#

i don't think it really matters on your end

north thistle
#

That is what I thought as well, in which case grouping the location data of multiple objects into some script only object sitting at 0,0,0 would remove that optimization, would it not?

minor gale
#

any optimization related to grouping probably wouldn't really be user facing, it'd be from the server to non-owners and isn't related to the bandwidth send limits on a player

#

like if your client decides it wants to send some variables to other players, it sends every variable on the object to the server, the server handles distributing it around; udon side you only care about the first part where it sends to the server because that's all you interact with

#

the only caveat would be if they somehow affect how often the owner can serialize a script based on the grouping, but i don't believe that really changes things (if you update the synced vars before they go out, you'd still end up sending the latest)

tawdry sequoia
#

my udon overlords

#

does anyone know how to sync something so it exactly happens on all clients at the same time

#

regardless of who triggered the event

fallow mountain
#

depends on what u r syncing

minor gale
# tawdry sequoia does anyone know how to sync something so it exactly happens on all clients at t...

in a basic sense something like this should work, it doesn't really require a synced float if the delay is consistent/known to all users

this doesn't lock out the event or have any handling for it being clicked multiple times, it's not the best way to do it if you want high precision

[SerializeField] private float timeDelay = 2f;
[UdonSynced] private float syncedDelay;

public override void Interact()
{
    CallDelayedEvent(timeDelay);
}

private void CallDelayedEvent(float timeFromNow)
{
    Networking.SetOwner(Networking.LocalPlayer, gameObject);
    syncedDelay = timeFromNow;
    RequestSerialization();

    SendCustomEventDelayedSeconds(nameof(_InvokeDelayedEvent), timeFromNow);
}
public void _InvokeDelayedEvent()
{
    // Do something here
}

public override void OnDeserialization(DeserializationResult result)
{
    float packetAge = Time.realtimeSinceStartup - result.sendTime;

    float invokeDelay = syncedDelay - packetAge;
    if (invokeDelay < 0f) return; // Discard events that have already fired
    SendCustomEventDelayedSeconds(nameof(_InvokeDelayedEvent), invokeDelay);
}
coarse verge
#

what's the difference

fallow mountain
coarse verge
#

epic

twin portal
#

a node doesn't exist for that function

#

either something deprecated, or something new?

#

ah, and this is what I expected to see

#

so I guess don't worry abt it

fallow mountain
minor gale
#

in my testing it's relative to server time and brought into the time space of the local player's time.realtimesincestartup

#

so yes you could use server time to trigger an event, but you'd still want to check delta imo rather than comparing absolute stamps (absolutes would be like checking if your current servertime >= target servertime)

minor gale
#

mostly because of wrapping, if the time was monotonic it wouldn't matter

#

if you were doing something like that, checking in update to see if it was time to fire an event, it's safer to calculate the time to fire and then increment or check against some local timespace than to check if the server time >= target time

fallow mountain
#

it should be monotonic for all intent and purposes, because it is a double, so it should be fine
and why check again? would servertime drift at all relative to localtime (why would it drift)? just trying to think of what circumstance would this happen

minor gale
#

because servertime wraps, even the double variant

#

when you say trigger an event at a specific servertime, i assume you mean checking if it's >= to see if it's time to do the action

i'm just saying that's not really wrap safe and you'd probably want to check the difference between target time and current time to see how long it will be before that event

fallow mountain
minor gale
#

GetServerTimeInSeconds()
and
GetServerTimeInMilliseconds()

these wrap, they should go from some high positive value to some negative value; it's not related to how long the instance has been up and can happen at any point, in practice it's not likely you'd encounter a ton of issues comparing absolutes, but it's not logically sound and if you're working in long delays it could be relevant

fallow mountain
#

can happen at any point? interesting (did a dev confirm this?)
so how would check delta work? so if the wrap happened at all, we abandon? since we don't know when it can happen?

minor gale
fallow mountain
#

so if delta > original delay, a wrap is detected... but since we dont know the value the wrap happened, we cannot calculate the new offset? i guess

minor gale
#

if you need to compare two timestamps for delta, you can use the method they provide for the seconds variant

CalculateServerDeltaTime(double timeInSeconds, double previousTimeInSeconds)

which should be wrap safe, or you can use the result.sendtime to calculate the delta assuming you're not using network events

fallow mountain
#

Ok looking at the comment again, i think he meant the GetServerTimeInMilliseconds() which is an int will wrap (as expected, in 24.86 days to be precise) if it stays up for months.
However I don't think this applies to GetServerTimeInSeconds() which is a double, which does not wrap around as per IEEE 754, and even if so it will take a significantly long time (the precision will only exceed 1 second after 285 million years) so I think we are totally wrap safe if we always use GetServerTimeInSeconds()
(Why is there two options to begin with?)

minor gale
#

it doesn't wrap in a value sense, they make it wrap

#

probably because of the int wrapping

fallow mountain
#

So the double is just converted from the int? lmao ok i see the problem
But at least we know how it wraps around

Use this value with care:
It can start with any positive value.
It will "wrap around" from 4294967.295 to 0!
Now just need to check if this is a constant, or if something else will make it wrap or jump to some random number...

minor gale
#

i don't know that those numbers are directly equivalent, but you could probably check and see if you ever load into an instance with a negative servertimeinseconds (which would depart from photon's default)

minor gale
#

you can also do something like this if you want to create a simple shared timespace, it's backed by servertime but wouldn't really wrap; you'd use syncedTime from that point on. not sure if my math is correct here, but it's this idea

[UdonSynced] private double startTime
private double offset;
private double syncedTime => Time.RealtimeSinceStartupAsDouble + offset;

void Start()
{
    if (Networking.IsOwner(gameObject))
    {
        RequestSerialization();
    }
}

public override OnPreserialization()
{
    startTime = Time.RealtimeSinceStartupAsDouble;
}

public override void OnDeserialization(DeserializationResult result)
{
    float packetAge = Time.realtimeSinceStartup - result.sendTime;

    offset = startTime - Time.RealtimeSinceStartupAsDouble + packetAge;
}
fallow mountain
#

Some digging around...

  • Looking around the 4,294,967,295 (which is max value in an unsigned 32-bit int) should be from networkingPeer.ServerTimeInMilliSeconds but it is unknown if it really returns an unsigned int.
  • BUT int ServerTimestamp from Photon docs (https://documentation.help/Photon-Unity-Networking-2/class_photon_1_1_pun_1_1_photon_network.html#af32d375b092832d574b3258f4de4d13f) is an int meaning it will wrap at 2,147,483,647 and this looks like latest docs.
  • And in Unity (and Udon) for our purposes this is also an int meaning it does not matter whether it was originally signed or unsigned, it will wrap at 2,147,483,647 to negative.
  • But it will still have 4,294,967,295 unique numbers, before actually "resetting".


  • So the question is, can the VRChat CalculateServerDeltaTime(double timeInSeconds, double previousTimeInSeconds) calculate wrap arounds, if yes, then problem solved.
#
  • Conclusion... it would seem we should always use the double GetServerTimeInSeconds() (which will go to +4,294,967,295 and resets to 0), and use CalculateServerDeltaTime to calculate the delta. To avoid the "first wrap around" of the int. Using this method we can safely go beyond the 24 days or so, until...
  • However, once the server exceeds 4294967.295 seconds i.e. 49 days, 17 hours, 2 minutes, and 47.295 seconds, there is a "second wrap around" which goes to zero. For both int and double. Which we will have to...
#

... make the judgement of whether the delta is >49 days.

minor gale
#

In other words, the probability that ServerTime will overflow after one hour is 0.083%. that's interesting, i'm glad someone's checked the wrap safety of their method

#

thank you for linking it. i actually use my own ntp scheme in my most recent worlds (clock syncing) because of some inaccuracy in servertime (this can be observed by measuring the one way trip times between two clients); though this usually doesn't exceed 20ms up or down and i haven't had many other people check the accuracy

fallow mountain
#

Note to self: I think what he meant was, there is a chance (0.083%) to load into an instance that is within 1 hour of an overflow (at 4294967.296 seconds), not that the server time will randomly overflow in an hour by pure chance.

minor gale
#

yeah it sounds like they were checking the probability you'd find an instance where that happened within an hour

fallow mountain
#

how do you even do NTP? does udon allow that?

minor gale
#

it's actually pretty simple, i use utc.now.ticks as the underlying space i exchange; you do two exchanges (an initial time from a remote client, a response from the host/source)

four timestamps are used

  1. initial send time
  2. receiver time
  3. receiver's response time
  4. initial sender's receive time

rtt and clock offset can be determined from these four stamps, i forget the formula, but it attempts to account for the time it takes for the source to respond (relative to the time they receive), which is why it records T2 and T3 instead of just T2 (response time)

fallow mountain
#

(so it needs allow untrusted url?)

minor gale
#

no i'm running this between clients with udonsynced variables

fallow mountain
#

oh i thought utc.now.ticks is an url

minor gale
#

it's actually a lot easier with custom network events, but you have less control over when those go out (as opposed to recording in preserialize)

#

it's like System.DateTime.UtcNow.Ticks

#

i forget the exact call

#

but i'm aligning system clocks

fallow mountain
#

so you are measuring the ping, basically

minor gale
#

yeah you can acquire the rtt from it very easily

fallow mountain
#

so you are mesuring the p2p ping, not just the server ping

#

I thought you meant, NTP as in, sync the time with some known accurate public NTP server

minor gale
#

p2p is the relevant part yeah, if you're doing something like extrapolation or need a very precise shared space it's more accurate than using servertime due to how they initially poll it once

#

i guess calling it a clock sync algorithm is more correct? it's the basis for ntp

fallow mountain
#

static void FetchServerTimestamp ( )
static

Refreshes the server timestamp (async operation, takes a roundtrip).

Can be useful if a bad connection made the timestamp unusable or imprecise.
(This is how clients get the server time I assume)

  • If I understand correctly, GetServerTimeInSeconds() gives you a timestamp that is about half a round trip time late.
  • In here https://vrc-beta-docs.netlify.app/worlds/udon/networking/network-stats/ says VRC.SDK3.Network.Stats can give you a RoundTripTime.
  • If we use both, we can maybe do some high precision sync. Maybe
    GetServerTimeInSeconds() + RoundTripTime / 2 can give you a "real time server time" to sync your stuff.
#
  • Or maybe "async operation, takes a roundtrip" means, the difference is literally a roundtrip. Idk.
minor gale
#

that initial fetch is done once on join and is the basis for servertime being inaccurate (modern netcode libraries align this periodically)

the rtt provided is actually more accurate as it's polled periodically and not really related to servertime afaik, but i don't believe you should use that rtt for anything between players

#

and no i don't believe polling the server time would be behind the server, they should attempt to align it to the server (the exact value) based on how long it takes for the timestamp to arrive (compensating for latency)

#

i'd expect the initial servertime you get is timestamp + rtt / 2 (already accounts for the time it took to arrive)

#

if you can find what pun actually does on join it'd be interesting to see

#

that fetch is not how clients acquire server time in an udon sense, you can poll servertime at any point, it doesn't pause your code or execute async

fallow mountain
#

oh yeah i meant, that is how the client gets it (and caches it) in the background, yeah we just do GetServerTimeInSeconds()
so yeah it seems GetServerTimeInSeconds() + rtt / 2 is solid, even gpt says it can understand the logic

#

interestingly .SDK3.Network.Stats is very new, released late april

minor gale
#

you can actually get some of the outbound data rates and stuff to make your own throttling now which is nice

#

as opposed to clogged/not clogged

fallow mountain
#

we can now shame people with bad ping

north thistle
# north thistle I *think* this should work, right? ```c# //returns true if the first network tim...

Decided to give this another crack.

//returns true if the first network time is sooner than the second, taking into account wrapping
public static bool IsNetworkTimeSooner(int firstTimeInMilliseconds, int secondTimeInMilliseconds)
{
    //check if a wraparound has occured
    if ((firstTimeInMilliseconds ^ secondTimeInMilliseconds) < 0)
    {
        return ((firstTimeInMilliseconds >> 30) & 1) > ((secondTimeInMilliseconds >> 30) & 1);
    }
    else
    {
        return firstTimeInMilliseconds < secondTimeInMilliseconds;
    }
}
fallow mountain
#

it is better to use GetServerTimeInSeconds() which virtually doubles the wrap around time to 49.7 days, and use CalculateServerDeltaTime(double timeInSeconds, double previousTimeInSeconds) to calculate difference

north thistle
#

Well if you're planning to have an instance open for weeks and plan to compare two timestamps weeks across, yah

#

But rule of cool, what I came up with seems pretty cool, at least to me personally

fallow mountain
#

are you just comparing the 30th bit, and is this intentional?

#

i think it won't work, say if the first is 1000 and second is -1000

#

it will return false

#

hmmm

#

what about first is 2147483647 and second is -2147483648, it will be false, but 2147483647 is sooner

north thistle
#

also

LogError($"Is {-190} before {100}: {MyUtilMethods.IsNetworkTimeSooner(-190, 10)}");
LogError($"Is {19} before {-10}: {MyUtilMethods.IsNetworkTimeSooner(19, -10)}");
LogError($"Is {int.MinValue + 190} before {int.MaxValue - 100}: {MyUtilMethods.IsNetworkTimeSooner(int.MinValue + 190, int.MaxValue - 10)}");
LogError($"Is {int.MaxValue - 19} before {int.MinValue + 10}: {MyUtilMethods.IsNetworkTimeSooner(int.MaxValue - 19, int.MinValue + 10)}");
LogError($"Is {-1000} before {-10}: {MyUtilMethods.IsNetworkTimeSooner(-1000, -10)}");
LogError($"Is {1000} before {10}: {MyUtilMethods.IsNetworkTimeSooner(1000, 10)}");

outputs:

fallow mountain
#

what happens with 1,073,741,823 and -1,073,741,824

#

which is earlier?_?

north thistle
#

that's a time span that pushes the limits of what you can accurately track before lack of information makes it impossible to tell

#

but that's also almost 25 days of real time, so not really an issue for 99.9% of use cases

fallow mountain
#

im still strugging to understand the 30 bit shift

north thistle
#

numbers approaching 0 from a negative will have the two highest bits of 11; numbers moving away from 0 towards positive will have the highest bits of 00

in turn, approaching int.MaxValue are 01 and moving away from int.MinValue is 10

#

thus we find that for numbers around zero the number with the higher 31st bit will be negative while for int.min/max it will be the positive number

frozen igloo
north thistle
#

since for the int.min/max the positive number is the earlier time and for around 0 the earlier is negative, we can look at this 31st bit to see which occurred first

fallow mountain
#

Phasedragon we are not talking precision at all, this is bit shifting, scroll up for the code

frozen igloo
#

yeah I know the code is ints, I misunderstood what vincil was saying. Sorry about that

fallow mountain
#

i think i grasp the concept, and it is cool, but maybe hard to read and understand for other people lmao

north thistle
#

lol yah, that is the downside

#

just think of it like floats: powered by black magic

fallow mountain
#

is it faster in performance, or is it pure playing with your bits

#

and btw, if you use the double you won't have to worry about signs, you will be forever in positive land

north thistle
#

I suspect 95%-99% of the performance consumption is going to be the method call itself because Udon, lol

#

but these are probably fast enough that you wouldn't be able to tell a difference unless you are calling it many times per frame

fallow mountain
#

check this

public static bool IsSooner(int a, int b)
{
    return (a ^ b) < 0 ? (a >> 30) > (b >> 30) : a < b;
}
#

gpt made it

north thistle
#

Clever; also not Udon compatible, lol

fallow mountain
#

wait, why not udon compatible

#

i dont see anything illegal

#

public static bool IsSooner(int a, int b) => (a ^ b) < 0 ? (a >> 30) > (b >> 30) : a < b;

silver hill
#

Alright, Im having a bit of an issue, my game works great solo, but whenever a player joins, the host experiences terrible lag.

I narrows it down to the players own hitbox as well as the enemies which is fine so what I did was change the logic in a way that it can work with manual sync instead of continuous, that helped a bit, but monsters using a manual sync dont really perform any networked events like attacks or anything, their position in the world is synced but thats it...

If I have the monsters work using a continuously synced Udon, it all works great, but the performance drop makes it unplayable (The joined players dont experience any lag by the way)

Was wondering if there is anything that I can do to improve performance while still having it continuous sync, or work with animations.

#

I set up the logic so that the host of the session does most of the legwork and all the attack ques would come from networked events, it works but it takes up alot more resources than I anticipated, unless Im just not optimised

fallow mountain
#

can u recap the main architecture of your thing, so you have monsters walking and attacking? and the owner is doing the monster logic?

north thistle
#

also T_T

#

looks like it's time for yet another layer of DataDictionary or List...

twin portal
#

the ternary conditional operator works

north thistle
#

oh I see; the previous time I had it not working, it was specifically in a way where it complained about "target-typed conditional expression"

#

so something like this doesn't work:
areaInfo[(int)AreaInfoType.NavArea] = isUsingNavAreaBounds ? info.navArea : info.floorTransforms;

silver hill
#

Whats confusing to me is even before the game starts, just waiting in the lobby area with nothing going on, when a player joins I go from 100fps to 70, like damn!

#

I found that the players persistent hitbox was causing some issues so I changed to logic around, changed it from a continuous sync to manual and its a bit better but still very odd

fallow mountain
#

players persistent hitbox <--why it needs sync? can it not move locally?

#

what does it do exactly

silver hill
#

Yes it moves locally, but it does more than just act as a hitbox, when a player gets downed itl spawn a "Revive" text above them, also tracks the player status to other players like "This player is dead"

#

And it spawns blood splats whenever a p[layer gets hit so networked events go off so people can see another player getting hurt etc

fallow mountain
#

this all sounds they can all run locally and dont need networking, except the dying part

silver hill
#

Oh I forgot to mention, the host depends on the hitboxes gameobject name to be case sensitive to detect if there are any alive players present else the game detects that it can start a mission failed scenario

#

So when a player is alive the hitbox is named "Activeplayer", a dead player is labaled "Deadplayer"

#

and the game does a "Isvalid" check to see if there are Activeplayers present in the world

fallow mountain
#

it may be easier if you just post your code... how many codes have you got?

silver hill
#

Just hitbox and a udon for each enemy type, they run using a very similar method

fallow mountain
#

using object name as bool is expensive i guess

silver hill
#

The performance is fine on the most part, and these issues only seem to affect the host, joined players get good FPS

fallow mountain
#

i guess u can isolate what is only run on the host and go from there

silver hill
#

I do 😛

#

But the performance is pretty poor once the game gets going

fallow mountain
#

can u post the code so i dont have to open unity lol

silver hill
#

The hitbox is split into 2 parts because of me switching it to use manual sync in an attempt to aleviate host performance issues

#

These are graphs :L

fallow mountain
#

oh...

silver hill
#

Lol, should have mentioned that

#

I have organised every part of them so they should be easilly readable

fallow mountain
#

hitbox 1 and 2 are on all players?

silver hill
#

Yeah,

#

Hitbox 1 is on the root game object for tracking the owners position

#

and then the rest is all on 2

fallow mountain
#

first problem is you are checking if owner is network local player every fixed update, multiple times... is this on a player object? so there will be one on every player?

silver hill
#

Yeah, its player object, so thats to tell it only the host should perform the operations

#

wait no

#

the owner, player yeah the owner of the hitbox

fallow mountain
#

ok that is the problem, every fixed update, many objects checking if local is owner, very very expensive
also, networking.get localplayer is expensive, best save it to a variable at start and only use the variable, (not that it will fix your problem, but is good practice)

silver hill
#

So make a bool "Is owned" or something and just use that as the branch truefalse?

fallow mountain
#

So hitbox 1 is just a follower to make it follow the player? can you name it as follower and/or add comment in the graph, to make it obvious what it does

fallow mountain
silver hill
#

Oh yes now that you say it I guess there is no need to check for owner when by default the owner is det when it spawns

fallow mountain
#

btw, in hitbox1, you dont even need a "SELF" variable, you can just leave any input unplugged and it will always be itself if unplugged

silver hill
#

Im not a programmer you see so I dont know how "expensive" things are, I assumed a isowner check wouldnt be that taxing but if you say it does Il take your word for it, perhaps making an onstart, assinging a bool "Isownedbylocalplayer" or something to then use that as the branch truefalse will be better?

silver hill
fallow mountain
#

thats ok i am just dumping all the improvements i can think of

silver hill
fallow mountain
#

let me go through hitbox2 to see why u need owners for

#

does the player2 hitbox needs to follow player2, on player1's client?

silver hill
#

Leme check what you're refering to

#

could you screen it?

fallow mountain
#

its nothing specific, just in general

#

is the hitbox for hit detection or something

silver hill
#

Everyones hitbox needs to be tracked onto themselves on everyones client yes

#

so that when a player goes down, an active player can walk up to them and revive them to get them back into the game

fallow mountain
#

i see ok

#

hitbox 1 can be something like this

silver hill
#

Interesting...

fallow mountain
#

um

#

delete it

silver hill
#

Eh?

fallow mountain
#

missed a link

silver hill
#

So youve set a variable for a fixed player, is that to reduce load finding the owner and then following the owner per update?

fallow mountain
#

yes

silver hill
#

I didnt think that would really improve the situation much but Il definitely try everything to make it faster

#

If only there was like a way you could see how much each node costs in power to run etc

fallow mountain
#

similarly in hitbox 2... i am saving two very often used variable

silver hill
#

I getchya, Il try that

#

I will do that for the monster scripts too

#

Have you had a look at the spider udon?

fallow mountain
#

yeah try to do that, and swap out the variables and checks etc first

#

not yet

silver hill
#

So for the monster udon logic I do tick cycles for the monster AI to run

#

Like once per second it goes through an AI cycle and every so often if the int is correct, itl perform a special attack etc

#

Its all host controlled so its great for joined players, not so great for the host lol

fallow mountain
#

also for (your own) readability sake, please dont make spiders, make it send a bunch of custom events instead

and if you need the same variable in many places, just make copies of them (also indication you should save a variable for those)

#

you can use block --> a list of custom events

silver hill
#

Oh yeah, its pretty messy due to me getting sick of scrolling back and forth lol

fallow mountain
#

for example

#

yeah i cant even read hitbox2 so i would wait until you do those cleanup... opening spider...

silver hill
#

And I avoid using duplicates of the same variable node due to udon slowing down the more nodes you have in the graph :L

#

Especially when Im getting near the 512 mark

#

Waiting 5 seconds per edit gets a bit much 😛

#

Luckily I can watch family guy or something while waiting inbetween 😛

fallow mountain
#

if it is too big try to split into multiple files and have like
SpiderMainController
SpiderAttacks (store attack events)
SpiderMovement (store movement events) etc...

silver hill
#

Been there, doing that 😛

fallow mountain
#

SpiderSound ... etc

#

yeah please do that because if it is hard to read, it is hard to help

#

make it easy for yourself and others lol

silver hill
#

Ye alot of the main features of the game use tons of objects to spread the load

fallow mountain
#

yeah its too much reading for the spider

#

i mean you have some good grouping and you try to lay them out which is good

#

but try to clean up a bit first, and do the caching variables first

#

then we can go from there

#

i only recently started learning udon sharp and the good thing about codes, you can tell gpt to write it for you

#

and clean up for you

#

and optimize for you

#

its like cheating

silver hill
#

I dont even trust chatGPT for basic stuff, like one time I asked it to help me balance my weapons in the game based on stats, give them a consistent power creep, I cant remember exactly what it did but it would constantly get things that I as a human would notice and be like "You cant use this number because you already declared it a different number in a previous sentence"

#

And then its like "You're totally right, it is incorrect, lets correct that for you..."

#

Only to get it wrong again

fallow mountain
#

yea i get that too, sometimes u have to handhold it, if it gets too wrong i have to delete the conversation and try again, hoping it has better RNG in its... understanding.

heavy spindle
#

I think Ai is good for suggestions but not for meticulous design

silver hill
#

Using Ai is like watching a person with only 1 arm and no legs swim, itl do it, but cant really work.

fallow mountain
#

(im afk for a while)

silver hill
#

No probs man, thanks for assisting so far

pulsar panther
#

Would anyone happen to know how to sync cloned objects even with late joiners?

#

Or at least with people in the world when spawned

tulip sphinx
#

generally its what vrc object pool is for

pulsar panther
#

Would need to be an array or just one

tulip sphinx
#

if you need to sync smth simpler than typical pickup, like idk, object that spawns once and then never to be moved, you may use some synced script to spawn it, where it just stores/syncs an array of transforms

tulip sphinx
#

in fact afaik the only unique thing pool does is allowing to have initially disabled networked objects without breaking

frozen igloo
#

Standard instantiated objects are not able to sync, but there are several other options.

If you need the ability to just 'spawn' a new object and don't necessarily need a fully unlimited number of those - then the best way to do that would be an objectpool. Each object inside doesn't need to be instantiated, they can just be disabled most of the time and then enabled as needed.

Alternatively, a common pattern in game dev is to spawn an object every time a player joins the instance, assign it to them, and then let them manage it. Things like hitboxes, nameplates, personal vehicles, high score management systems, etc. If that doesn't sound like what you want, then just use a standard object pool or something else. If that does sound like what you're trying to do, you should use PlayerObjects, which are a dedicated sytem provided by VRChat for that specific use case

pulsar panther
#

If I use it with instantiate and add the clones to the pool will the clones sync

frozen igloo
pulsar panther
#

Ok

cold laurel
# fallow mountain its like cheating

You may think that, but I can guarantee that the code you're ending up with is of extremely poor quality.

If you insist on using AI to learn programming then please never copy paste the generated output into your script. Read it, understand it, and then write it yourself.

cold laurel
# fallow mountain and optimize for you

There is no language model on earth that understands anything.
All they can do is predict what's likely to come next based on their training data.

UdonSharp optimization is an extremely niche topic that is extremely unlikely to be in the AI's dataset. And even if there is, it'll be so little compared to how much other programming related data there is.

fallow mountain
#

its like autocomplete on steriods and saves me typing, i just tell it what i want to change

cold laurel
#

AI can only ever suggest extremely mediocre code.

fallow mountain
#

if a code does what i want, how i want, and fast, and readable, it is arguably good quality, it is just writing what i want for me

#

and yes, it doesnt code automatically, i do hand hold a lot

#

it's another tool, and it can make good quality code, if you hand hold it

cold laurel
#

You're neglecting the most important aspect of software engineering. Maintainability.

fallow mountain
#

how so?

#

i tell it to comment properly etc and format properly etc

cold laurel
#

AI code is very often all over the place when it comes to following standards and conventions.
It often includes completely unnecessary parts that at best do nothing, and at worst waste performance.
If you don't ensure you understand everything the AI generated for you every single time then you'll quickly end up with a codebase that "just works" if you don't poke it too hard.

#

Sure, if you have a very specific problem that a lot of people have solved before. And they have posted their answers online. And the problem can be solved in a handful of lines, then you can use a chatbot to probably get an okay output!

fallow mountain
#

yeah you have to on top of it

#

and poke it from every angle

#

and make it do things for you how you want

cold laurel
#

Exactly, you have to already know what you want so that you can determine if the AI gave you what you want.

#

Which makes it a dangerous tool for less experienced devs.

fallow mountain
#

dangerous but powerful but dangerous

#

= fun

#

lol

cold laurel
#

If I hand you an F-35 fighter jet, would you be able to use it?
Sure, it might be "powerful" and "dangerous". But only in the right hands.

tulip sphinx
#

not like were building nuclear reactors here, worst that will suffer is dev's reputation

cold laurel
#

And the players of whatever you make.

fallow mountain
#

i just tell it to explain what it is doing all the time, and why, and use it to research and present and interrogate itself, and i learn during the process... and if something smells off, i delete the conversation and restart with new RNG, and i reroll until it makes sense
it's f-35 that flies for you, in a way, you dont have to micro everything

#

and you stay high level and learning stuff, and let it do all the leg work, which works out suprisingly okay so far, granted i havnt made serious complicated stuff but that is my impression

#

you learn why and how it is flying and you actively monitor how it is flying, you can ask questions etc, you can step in any moment to tweak things, etc... i mean it went from looking jank to looking optimistic from my experience, so i guess it got better recently

#

but it is very jank at udon, yes, but even including debug it is quicker than if i write stuff myself

cold laurel
#

It's like hiring a Boston Dynamics robot to run on the treadmill for you so you can get fit. You ask it to explain what it's doing and how to get good at running and it'll go on and on for you while you sit and watch.

cold laurel
#

Please just try to actually run on the treadmill alone every once in a while. Turn the bot off and see if you remember what to do.

fallow mountain
#

i wouldnt compare it to running on treadmill for you because the work is not the goal here... the goal is just getting some code, maybe a self driving car is a better analogy lol, you dont manually drive it, but you can watch and direct it and push buttons to correct it etc

#

it is very good for people who dont type very fast like me, it types for me

#

it is a car, in a way, you lose some leg muscles because you don't walk anymore, but you could go faster and further, if you drive it and dont crash... a car or bicycle, i would say

#

dangerous, yes, potentially very

#

like real cars

#

but it's not all bad, is my experience

#

(scary to think we all traded muscles for machines in the past, and now, mental capacities for advanced calculators)

cold laurel
#

If the goal is always "just getting some code" and not "learning how to code", then you will forever be stuck as a passenger of the self driving car.

fallow mountain
#

on some level, that may be where things are headed, like how cars made walking obsolete (for long distances)... i dont disagree walking is great and good for you, but cars are great too even as a passenger in a self driving car
i guess it is just a matter of pros and cons

cold laurel
fallow mountain
#

walking is great and always will be the fallback yes i agree

#

(but im too lazy to train my muscles)

cold laurel
#

Then you will never be great.

fallow mountain
#

i will never be fit, i agree

cold laurel
#

I'm glad we have reached a consensus.

fallow mountain
#

yes, interesting conversation i have to say

#

actually made me even more excited, AI is jank now, but it has that funny smell similar to early cars that are wobbly and fragile... and looking at cars now, now it may be that weird historical window where we get to taste this janky AI, because it is evolving at quite dramatic speed... this could be some rare vintage AI experience😂

timber ferry
#

i like using GPT for some quick help with my small issues. i recently started using it as a tool for coding rather than seeing what it can make, and it’s really helpful for small snippets that i couldn’t quite remember how to make, it where i wasn’t quite sure how to write it.

for example, time. i find Time.time difficult for some reason, so if i explain to it what i want, eg. a timer that runs every second, it’ll give me a couple lines in an update method that reminds me “ooh right, yeah that works”

or if i have something i want a button to do, i can explain to it what that something is, and it gives me a starting point and i get some ideas off it on how i can write the functionality myself

#

all in all, i think it’s a really great tool, but i’d never use it to generate a whole script or method for me

strange token
#

I have used it to generate methods when I can’t wrap my head around what I’m trying to accomplish and it showing it’s work automatically teaches me but I guess that’s because I already understand so much myself I can just read the code and understand most of it 😅

#

I also used it to learn HLSL myself by generating methods and messing with them myself until I understood what was happening and could optimize the shader manually, really neat but it definitely did not make a working shader on its own lmao

#

So far anything I wanna learn is ez af with GPT since I get to see all the steps broken down super detailed

timber ferry
#

exactly! at this point i can actually read the code and understand what it’s doing, so rather than mindlessly copy pasting, i can look over what it generated and figure out how to implement it in my own script. it’s helpful knowing what you’re doing too, because it’s definitely wrong sometimes.

and yeah, it’s not very good at shaders. the only shader i could get it to write that worked and didn’t look too bad code-wise, was a shader that was literally just color and an emission strength value

tulip sphinx
#

its nice to automate routine stuff at least. i needed very simple vertex local wave shader, and while i def can do it manually (clueless), spending 10 seconds to type prompt and getting exactly what i want instead of debugging def feels nice. same with simple unity stuff, like idk, take three transforms and find their middle point. but i dont trust it with udon/vrc parts, not much to train on compared to unity/c#

finite sierra
#

honestly AI wont help you improve. since the solution is created for you. even if it's a very small part of it. you would get further by just sitting down and trying to run things by trial and error. or if you are the person who learns from reading then read a few programming books. there is too much AI garbage out there and it's a stain upon game developement and software development in general.

strange token
#

Yeah but not everyone has the brain capacity to just get into the nitty and gritty for hours to figure it out themselves

finite sierra
#

then i really do need to ask whats the purpose of using it then?

fallow mountain
#

i dont need to worry about typing out every single line of the code, i can focus on the mechanics design and the general structure and implementation strategies of the code, and offload the writing part to gpt, so i dont have to think about indents and checking brackets and typos and all those tedious works

#

i can go in and micromanage something if needed, but for the most part, it is doing the physical writing, but im still the director and author, if it makes sense, it is really just like autocomplete, just a bit smarter

#

you have IDEs that can highlight stuff and spell check and suggest and autocomplete and all those fancy things... it is similar, just more automatic and less manual typing

#

and it can suggest methods, strategies, structures, it can comment code and analysis code and report to you, you learn and brainstorm and test faster, you write faster, you debug faster, etc

#

at least for me it is faster than me researching and typing it

#

the only pitfall is it makes mistakes, and you have to spot them

#

so you have to be the one driving the thing, and not let it run you around in circles

finite sierra
cold laurel
finite sierra
tulip sphinx
#

outside of udon you just import libraries/packages and run isOdd(1) (or app.listen, or torch.device('cuda:0')), you dont care how its doing it, you dont even care if its doing it correctly all the time or there are some edge cases and it just happens to work for your setup. So ye, ai will fail on global creative tasks and youd have to deconstruct them into smaller ones, and youd have to debug them, but its still faster and for most people will produce generally better code as well.

north thistle
#

The only time I've explicitly asked an an AI "how do i do this?" I made sure to understand what it was doing and then rewrote the code in my own style.

Outside that I've only used it for its autocomplete feature; which is amazing when it works, I just wish it worked more often, lol

fallow mountain
#

does everything in vrchat go though the server, including voice? or is voice p2p?

tulip sphinx
#

all through server

#

only non p2h is urls

fallow mountain
#

oh nevermind, it is in beta sdk

north thistle
#

Are the VRCPickup methods like "OnDrop" network callable? They are public methods but they're also special methods.

fallow mountain
#

i dont think so, it is "custom" network event only
but there is nothing stopping you from doing customevent->drop it

north thistle
#

I'm mostly wanting to know if I need to manually add protections to it since adding a _ prefix isn't an option

fallow mountain
#

protection from what exactly?

north thistle
#

malicious network calls

fallow mountain
#

i didnt even know there is such thing, i mean if people hack, they will find a way